@antelopejs/dms-ai 0.1.4 → 0.1.6
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 +55 -29
- package/dist/channels/bridge.d.ts +2 -8
- package/dist/channels/bridge.js +26 -42
- package/dist/channels/bridge.js.map +1 -1
- package/dist/channels/channel-service.d.ts +2 -2
- package/dist/channels/channel-service.js +12 -31
- package/dist/channels/channel-service.js.map +1 -1
- package/dist/channels/registry.d.ts +2 -2
- package/dist/channels/registry.js +7 -7
- package/dist/channels/registry.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/constants/channels.d.ts +5 -10
- package/dist/constants/channels.js +6 -10
- package/dist/constants/channels.js.map +1 -1
- package/dist/constants/sidecar.d.ts +5 -1
- package/dist/constants/sidecar.js +6 -2
- package/dist/constants/sidecar.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/build-id.d.ts +1 -0
- package/dist/lifecycle/build-id.js +32 -0
- package/dist/lifecycle/build-id.js.map +1 -0
- package/dist/lifecycle/sidecar-owner.d.ts +13 -0
- package/dist/lifecycle/sidecar-owner.js +89 -0
- package/dist/lifecycle/sidecar-owner.js.map +1 -0
- package/dist/lifecycle/sidecar-socket.d.ts +1 -1
- package/dist/lifecycle/sidecar-socket.js +2 -2
- package/dist/lifecycle/sidecar-socket.js.map +1 -1
- package/dist/lifecycle/spawn-sidecar.d.ts +0 -1
- package/dist/lifecycle/spawn-sidecar.js +51 -159
- package/dist/lifecycle/spawn-sidecar.js.map +1 -1
- package/dist/lifecycle/terminate-process.d.ts +1 -0
- package/dist/lifecycle/terminate-process.js +34 -0
- package/dist/lifecycle/terminate-process.js.map +1 -0
- package/dist/routes/channels.d.ts +3 -3
- package/dist/routes/channels.js +11 -12
- package/dist/routes/channels.js.map +1 -1
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/index.js +0 -1
- package/dist/routes/index.js.map +1 -1
- package/frontend-vue/.prettierignore +3 -0
- package/frontend-vue/.prettierrc +10 -0
- package/frontend-vue/app/chat/components/ChatView.vue +582 -0
- package/frontend-vue/app/chat/components/ComposerInput.vue +742 -0
- package/frontend-vue/app/chat/components/ConnectionBanner.vue +105 -0
- package/frontend-vue/app/chat/components/ConversationDrawer.vue +266 -0
- package/frontend-vue/app/chat/components/MarkdownContent.vue +196 -0
- package/frontend-vue/app/chat/components/MessageList.vue +474 -0
- package/frontend-vue/app/chat/components/PermissionRequests.vue +323 -0
- package/frontend-vue/app/chat/components/QuestionPrompt.vue +275 -0
- package/frontend-vue/app/chat/components/QueuedMessages.vue +109 -0
- package/frontend-vue/app/chat/components/TodoList.vue +291 -0
- package/frontend-vue/app/chat/components/ToolCallEntry.vue +218 -0
- package/frontend-vue/app/chat/components/ToolCluster.vue +282 -0
- package/frontend-vue/app/chat/composables/useChatChannel.ts +116 -0
- package/frontend-vue/app/chat/composables/useConversation.ts +838 -0
- package/frontend-vue/app/chat/composables/useConversationId.ts +34 -0
- package/frontend-vue/app/chat/composables/useConversationList.ts +56 -0
- package/frontend-vue/app/chat/composables/usePermissionQueue.ts +59 -0
- package/frontend-vue/app/chat/composables/useQuestionQueue.ts +50 -0
- package/frontend-vue/app/chat/composables/useRunClock.ts +28 -0
- package/frontend-vue/app/chat/composables/useSettings.ts +67 -0
- package/frontend-vue/app/chat/constants/attachments.ts +16 -0
- package/frontend-vue/app/chat/constants/connection-banner.ts +7 -0
- package/frontend-vue/app/chat/constants/conversation-drawer.ts +13 -0
- package/frontend-vue/app/chat/constants/conversation.ts +75 -0
- package/frontend-vue/app/chat/constants/permissions.ts +50 -0
- package/frontend-vue/app/chat/constants/protocol.ts +48 -0
- package/frontend-vue/app/chat/constants/run-status.ts +50 -0
- package/frontend-vue/app/chat/constants/settings.ts +60 -0
- package/frontend-vue/app/chat/types/conversation.ts +87 -0
- package/frontend-vue/app/chat/types/permission.ts +7 -0
- package/frontend-vue/app/chat/types/question.ts +17 -0
- package/frontend-vue/app/chat/types/settings.ts +27 -0
- package/frontend-vue/app/chat/utils/attachments.ts +62 -0
- package/frontend-vue/app/chat/utils/format.ts +14 -0
- package/frontend-vue/app/chat/utils/ids.ts +34 -0
- package/frontend-vue/app/chat/utils/markdown.ts +188 -0
- package/frontend-vue/app/chat/utils/run-status.ts +90 -0
- package/frontend-vue/app/chat/utils/todos.ts +64 -0
- package/frontend-vue/app/chat/utils/tool-summary.ts +53 -0
- package/frontend-vue/app/components/ActivityView.vue +34 -34
- package/frontend-vue/app/components/ChatPanel.vue +276 -0
- package/frontend-vue/app/components/SettingsView.vue +120 -112
- package/frontend-vue/app/components/SkillsView.vue +55 -53
- package/frontend-vue/app/plugins/ai.client.ts +145 -129
- package/frontend-vue/app/runtime/assistant-session.ts +21 -0
- package/frontend-vue/app/runtime/backoff.ts +11 -14
- package/frontend-vue/app/runtime/channel-client.ts +130 -141
- package/frontend-vue/app/runtime/chat-transport.ts +25 -55
- package/frontend-vue/app/runtime/constants.ts +69 -59
- package/frontend-vue/app/runtime/current-page.ts +49 -15
- package/frontend-vue/app/runtime/event-stream.ts +35 -35
- package/frontend-vue/app/runtime/frame-router.ts +30 -0
- package/frontend-vue/app/runtime/header-action.ts +18 -18
- package/frontend-vue/app/runtime/host-commands.ts +57 -56
- package/frontend-vue/app/runtime/listeners.ts +31 -0
- package/frontend-vue/app/runtime/navigation-complete.ts +7 -7
- package/frontend-vue/app/runtime/overlay-prefs.ts +36 -36
- package/frontend-vue/app/runtime/panel-state.ts +88 -0
- package/frontend-vue/app/runtime/session-gate.ts +8 -8
- package/frontend-vue/app/runtime/shortcuts.ts +12 -12
- package/frontend-vue/app/runtime/sidecar-status.ts +61 -61
- package/frontend-vue/app/runtime/typed-message.ts +11 -0
- package/frontend-vue/dms.frontend.ts +12 -12
- package/frontend-vue/eslint.config.mjs +13 -13
- package/frontend-vue/package.json +11 -4
- package/frontend-vue/pnpm-lock.yaml +298 -96
- package/frontend-vue/tests/backoff.test.ts +26 -30
- package/frontend-vue/tests/channel-client.test.ts +187 -188
- package/frontend-vue/tests/chat/markdown.test.ts +271 -0
- package/frontend-vue/tests/chat/useChatChannel.test.ts +124 -0
- package/frontend-vue/tests/chat/useConversation.test.ts +283 -0
- package/frontend-vue/tests/chat-panel.test.ts +354 -0
- package/frontend-vue/tests/chat-transport.test.ts +84 -57
- package/frontend-vue/tests/host-commands.test.ts +126 -87
- package/frontend-vue/tests/navigation.test.ts +65 -34
- package/frontend-vue/tests/overlay-prefs.test.ts +49 -49
- package/frontend-vue/tests/panel-state.test.ts +58 -0
- package/frontend-vue/tests/session-gate.test.ts +33 -33
- package/frontend-vue/tests/sidecar-guest.test.ts +46 -46
- package/frontend-vue/tests/sidecar-status.test.ts +29 -29
- package/frontend-vue/tsconfig.chat.json +16 -0
- package/frontend-vue/vitest.config.ts +6 -4
- package/package.json +6 -5
- package/sidecar/dist/agent/effective-mode.d.ts +2 -2
- package/sidecar/dist/agent/effective-mode.js +1 -1
- package/sidecar/dist/agent/effective-mode.js.map +1 -1
- package/sidecar/dist/agent/host-context.js +7 -9
- package/sidecar/dist/agent/host-context.js.map +1 -1
- package/sidecar/dist/agent/permission-bus.d.ts +1 -1
- package/sidecar/dist/agent/permission-bus.js +2 -2
- package/sidecar/dist/agent/permission-bus.js.map +1 -1
- package/sidecar/dist/agent/provider.d.ts +2 -2
- package/sidecar/dist/agent/question-bus.d.ts +1 -1
- package/sidecar/dist/agent/question-bus.js +2 -2
- package/sidecar/dist/agent/question-bus.js.map +1 -1
- package/sidecar/dist/agent/runner.d.ts +2 -2
- package/sidecar/dist/agent/runner.js +24 -8
- package/sidecar/dist/agent/runner.js.map +1 -1
- package/sidecar/dist/agent/session.d.ts +2 -2
- package/sidecar/dist/agent/session.js +49 -9
- package/sidecar/dist/agent/session.js.map +1 -1
- package/sidecar/dist/agent/switching-runner.js +10 -5
- package/sidecar/dist/agent/switching-runner.js.map +1 -1
- package/sidecar/dist/agent/turn-end-reasons.d.ts +2 -0
- package/sidecar/dist/agent/turn-end-reasons.js +6 -1
- package/sidecar/dist/agent/turn-end-reasons.js.map +1 -1
- package/sidecar/dist/constants/agent.d.ts +4 -1
- package/sidecar/dist/constants/agent.js +4 -1
- package/sidecar/dist/constants/agent.js.map +1 -1
- package/sidecar/dist/constants/cli.d.ts +0 -1
- package/sidecar/dist/constants/cli.js +0 -1
- package/sidecar/dist/constants/cli.js.map +1 -1
- package/sidecar/dist/constants/codex.d.ts +5 -2
- package/sidecar/dist/constants/codex.js +5 -2
- package/sidecar/dist/constants/codex.js.map +1 -1
- package/sidecar/dist/constants/http.d.ts +0 -9
- package/sidecar/dist/constants/http.js +0 -19
- package/sidecar/dist/constants/http.js.map +1 -1
- package/sidecar/dist/constants/mcp.d.ts +1 -1
- package/sidecar/dist/constants/mcp.js +1 -1
- package/sidecar/dist/constants/mcp.js.map +1 -1
- package/sidecar/dist/constants/paths.d.ts +0 -1
- package/sidecar/dist/constants/paths.js +0 -1
- package/sidecar/dist/constants/paths.js.map +1 -1
- package/sidecar/dist/constants/ws.d.ts +3 -4
- package/sidecar/dist/constants/ws.js +3 -4
- package/sidecar/dist/constants/ws.js.map +1 -1
- package/sidecar/dist/index.d.ts +4 -1
- package/sidecar/dist/index.js +13 -13
- package/sidecar/dist/index.js.map +1 -1
- package/sidecar/dist/mcp/tools/find-pages-using.js +5 -10
- package/sidecar/dist/mcp/tools/find-pages-using.js.map +1 -1
- package/sidecar/dist/mcp/types.d.ts +1 -1
- package/sidecar/dist/pages/page-filepath.d.ts +4 -0
- package/sidecar/dist/pages/page-filepath.js +28 -0
- package/sidecar/dist/pages/page-filepath.js.map +1 -0
- package/sidecar/dist/prompts/agent.d.ts +1 -1
- package/sidecar/dist/prompts/agent.js +1 -1
- package/sidecar/dist/prompts/agent.js.map +1 -1
- package/sidecar/dist/protocol/events.js +2 -2
- package/sidecar/dist/protocol/events.js.map +1 -1
- package/sidecar/dist/protocol/messages.d.ts +4 -15
- package/sidecar/dist/protocol/messages.js +4 -11
- package/sidecar/dist/protocol/messages.js.map +1 -1
- package/sidecar/dist/providers/claude/config.d.ts +2 -2
- package/sidecar/dist/providers/claude/config.js +2 -2
- package/sidecar/dist/providers/claude/config.js.map +1 -1
- package/sidecar/dist/providers/claude/provider.js +1 -1
- package/sidecar/dist/providers/claude/provider.js.map +1 -1
- package/sidecar/dist/providers/codex/config.d.ts +3 -2
- package/sidecar/dist/providers/codex/config.js +3 -3
- package/sidecar/dist/providers/codex/config.js.map +1 -1
- package/sidecar/dist/providers/codex/process.js +107 -74
- package/sidecar/dist/providers/codex/process.js.map +1 -1
- package/sidecar/dist/providers/codex/provider.js +68 -31
- package/sidecar/dist/providers/codex/provider.js.map +1 -1
- package/sidecar/dist/providers/codex/skills.d.ts +1 -0
- package/sidecar/dist/providers/codex/skills.js +4 -1
- package/sidecar/dist/providers/codex/skills.js.map +1 -1
- package/sidecar/dist/providers/codex/stop.d.ts +4 -0
- package/sidecar/dist/providers/codex/stop.js +71 -0
- package/sidecar/dist/providers/codex/stop.js.map +1 -0
- package/sidecar/dist/server/{iframe-socket-registry.d.ts → chat-socket-registry.d.ts} +3 -2
- package/sidecar/dist/server/{iframe-socket-registry.js → chat-socket-registry.js} +4 -3
- package/sidecar/dist/server/chat-socket-registry.js.map +1 -0
- package/sidecar/dist/server/client-auth.js +2 -14
- package/sidecar/dist/server/client-auth.js.map +1 -1
- package/sidecar/dist/server/host-command-router.d.ts +5 -0
- package/sidecar/dist/server/host-command-router.js +4 -0
- package/sidecar/dist/server/host-command-router.js.map +1 -0
- package/sidecar/dist/server/host-socket-registry.d.ts +1 -1
- package/sidecar/dist/server/host-socket-registry.js +16 -13
- package/sidecar/dist/server/host-socket-registry.js.map +1 -1
- package/sidecar/dist/server/http.d.ts +0 -1
- package/sidecar/dist/server/http.js +1 -44
- package/sidecar/dist/server/http.js.map +1 -1
- package/sidecar/dist/server/routing.d.ts +5 -6
- package/sidecar/dist/server/routing.js +18 -28
- package/sidecar/dist/server/routing.js.map +1 -1
- package/sidecar/dist/server/ws.d.ts +2 -2
- package/sidecar/dist/server/ws.js +41 -39
- package/sidecar/dist/server/ws.js.map +1 -1
- package/sidecar/dist/state/host-state.d.ts +10 -2
- package/sidecar/dist/state/host-state.js +13 -3
- package/sidecar/dist/state/host-state.js.map +1 -1
- package/sidecar/dist/state/settings-store.js +2 -2
- package/sidecar/dist/state/settings-store.js.map +1 -1
- package/sidecar/dist/state/settings-types.d.ts +3 -3
- package/sidecar/dist/state/settings-types.js +1 -1
- package/sidecar/dist/state/settings-types.js.map +1 -1
- package/sidecar/package.json +3 -5
- package/dist/chatbox/passthrough.d.ts +0 -3
- package/dist/chatbox/passthrough.js +0 -53
- package/dist/chatbox/passthrough.js.map +0 -1
- package/dist/constants/chatbox.d.ts +0 -10
- package/dist/constants/chatbox.js +0 -35
- package/dist/constants/chatbox.js.map +0 -1
- package/dist/routes/chatbox.d.ts +0 -7
- package/dist/routes/chatbox.js +0 -47
- package/dist/routes/chatbox.js.map +0 -1
- package/frontend-vue/app/runtime/overlay.ts +0 -400
- package/frontend-vue/app/runtime/theme-bridge.ts +0 -189
- package/sidecar/chatbox/dist/assets/index-C8u7mkHn.css +0 -1
- package/sidecar/chatbox/dist/assets/index-tdZvY7ee.js +0 -52
- package/sidecar/chatbox/dist/index.html +0 -13
- package/sidecar/dist/server/iframe-socket-registry.js.map +0 -1
|
@@ -3,43 +3,45 @@ import {
|
|
|
3
3
|
createBackoff,
|
|
4
4
|
nextDelay,
|
|
5
5
|
resetBackoff,
|
|
6
|
-
} from
|
|
6
|
+
} from "./backoff";
|
|
7
7
|
import {
|
|
8
|
+
CHANNEL_EVENT_FRAME,
|
|
8
9
|
CHANNEL_EVENT_READY,
|
|
9
|
-
|
|
10
|
-
CHANNEL_LIST_SEPARATOR,
|
|
10
|
+
CHANNEL_EVENTS_PATH,
|
|
11
11
|
CHANNEL_MESSAGES_SEGMENT,
|
|
12
|
+
CHANNEL_PATH,
|
|
12
13
|
CHANNEL_RECONNECT_DELAYS_MS,
|
|
13
14
|
CHANNEL_STATUS_CONNECTED,
|
|
14
15
|
CHANNEL_STATUS_CONNECTING,
|
|
15
16
|
CHANNEL_STATUS_DISCONNECTED,
|
|
16
17
|
CHANNEL_STATUS_RECONNECTING,
|
|
17
|
-
CHANNELS_PATH,
|
|
18
18
|
EVENT_STREAM_TYPE,
|
|
19
|
-
} from
|
|
20
|
-
import { readEventStream, type StreamEvent } from
|
|
19
|
+
} from "./constants";
|
|
20
|
+
import { readEventStream, type StreamEvent } from "./event-stream";
|
|
21
21
|
|
|
22
22
|
export type ChannelStatus =
|
|
23
23
|
| typeof CHANNEL_STATUS_CONNECTING
|
|
24
24
|
| typeof CHANNEL_STATUS_CONNECTED
|
|
25
25
|
| typeof CHANNEL_STATUS_DISCONNECTED
|
|
26
|
-
| typeof CHANNEL_STATUS_RECONNECTING
|
|
26
|
+
| typeof CHANNEL_STATUS_RECONNECTING;
|
|
27
27
|
|
|
28
28
|
/** Posts one message as JSON; `$authFetch` in the dashboard refreshes a stale session. */
|
|
29
|
-
export type PostMessage = (path: string, body: unknown) => Promise<unknown
|
|
29
|
+
export type PostMessage = (path: string, body: unknown) => Promise<unknown>;
|
|
30
30
|
|
|
31
|
-
/** Opens the event stream
|
|
32
|
-
export type OpenStream = (
|
|
31
|
+
/** Opens the tab's event stream. */
|
|
32
|
+
export type OpenStream = (
|
|
33
|
+
path: string,
|
|
34
|
+
signal: AbortSignal,
|
|
35
|
+
) => Promise<Response>;
|
|
33
36
|
|
|
34
37
|
export interface ChannelClientOptions {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
onStatusChange?: (status: ChannelStatus) => void
|
|
38
|
+
post: PostMessage;
|
|
39
|
+
openStream?: OpenStream;
|
|
40
|
+
/** Every connection is a new sidecar socket: identify on it here. */
|
|
41
|
+
onReady?: () => void;
|
|
42
|
+
/** Raw sidecar frame, as the socket delivered it. */
|
|
43
|
+
onFrame?: (raw: string) => void;
|
|
44
|
+
onStatusChange?: (status: ChannelStatus) => void;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export interface ChannelClient {
|
|
@@ -47,219 +49,206 @@ export interface ChannelClient {
|
|
|
47
49
|
* Queued and posted one at a time, in order. False when it was dropped
|
|
48
50
|
* because the stream is not connected, so the caller knows nothing left.
|
|
49
51
|
*/
|
|
50
|
-
send: (
|
|
51
|
-
isConnected: () => boolean
|
|
52
|
-
getStatus: () => ChannelStatus
|
|
52
|
+
send: (msg: unknown) => boolean;
|
|
53
|
+
isConnected: () => boolean;
|
|
54
|
+
getStatus: () => ChannelStatus;
|
|
53
55
|
/** Opens the stream if it is not open yet. */
|
|
54
|
-
start: () => void
|
|
56
|
+
start: () => void;
|
|
55
57
|
/** Closes the stream until the next `start`. */
|
|
56
|
-
stop: () => void
|
|
58
|
+
stop: () => void;
|
|
57
59
|
/**
|
|
58
60
|
* Drops any pending backoff and replaces the stream at once, if started.
|
|
59
61
|
* The status says connecting until the new stream is ready, since nothing
|
|
60
62
|
* can be sent in between.
|
|
61
63
|
*/
|
|
62
|
-
reconnectNow: () => void
|
|
64
|
+
reconnectNow: () => void;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
interface ReadyPayload {
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
interface QueuedMessage {
|
|
70
|
-
channel: string
|
|
71
|
-
msg: unknown
|
|
68
|
+
connectionId?: unknown;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
interface ClientState {
|
|
75
|
-
options: ChannelClientOptions
|
|
76
|
-
status: ChannelStatus
|
|
77
|
-
|
|
78
|
-
stream: AbortController | null
|
|
79
|
-
retryTimer: ReturnType<typeof setTimeout> | null
|
|
80
|
-
queue:
|
|
81
|
-
isDraining: boolean
|
|
82
|
-
isStarted: boolean
|
|
83
|
-
backoff: BackoffState
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function channelEventsPath(channels: readonly string[]): string {
|
|
87
|
-
return `${CHANNELS_PATH}/${channels.join(CHANNEL_LIST_SEPARATOR)}/${CHANNEL_EVENTS_SEGMENT}`
|
|
72
|
+
options: ChannelClientOptions;
|
|
73
|
+
status: ChannelStatus;
|
|
74
|
+
connectionId: string | null;
|
|
75
|
+
stream: AbortController | null;
|
|
76
|
+
retryTimer: ReturnType<typeof setTimeout> | null;
|
|
77
|
+
queue: unknown[];
|
|
78
|
+
isDraining: boolean;
|
|
79
|
+
isStarted: boolean;
|
|
80
|
+
backoff: BackoffState;
|
|
88
81
|
}
|
|
89
82
|
|
|
90
83
|
export function channelMessagesPath(connectionId: string): string {
|
|
91
|
-
return `${
|
|
84
|
+
return `${CHANNEL_PATH}/${encodeURIComponent(connectionId)}/${CHANNEL_MESSAGES_SEGMENT}`;
|
|
92
85
|
}
|
|
93
86
|
|
|
94
87
|
function openEventStream(path: string, signal: AbortSignal): Promise<Response> {
|
|
95
88
|
return fetch(path, {
|
|
96
89
|
headers: { accept: EVENT_STREAM_TYPE },
|
|
97
|
-
credentials:
|
|
98
|
-
cache:
|
|
90
|
+
credentials: "same-origin",
|
|
91
|
+
cache: "no-store",
|
|
99
92
|
signal,
|
|
100
|
-
})
|
|
93
|
+
});
|
|
101
94
|
}
|
|
102
95
|
|
|
103
96
|
function setStatus(state: ClientState, next: ChannelStatus): void {
|
|
104
|
-
if (state.status === next) return
|
|
105
|
-
state.status = next
|
|
106
|
-
state.options.onStatusChange?.(next)
|
|
97
|
+
if (state.status === next) return;
|
|
98
|
+
state.status = next;
|
|
99
|
+
state.options.onStatusChange?.(next);
|
|
107
100
|
}
|
|
108
101
|
|
|
109
|
-
function
|
|
102
|
+
function readConnectionId(data: string): string | null {
|
|
110
103
|
try {
|
|
111
|
-
const {
|
|
112
|
-
|
|
113
|
-
const entries = Object.entries(connections).filter(
|
|
114
|
-
(entry): entry is [string, string] => typeof entry[1] === 'string',
|
|
115
|
-
)
|
|
116
|
-
return Object.fromEntries(entries)
|
|
104
|
+
const { connectionId } = JSON.parse(data) as ReadyPayload;
|
|
105
|
+
return typeof connectionId === "string" ? connectionId : null;
|
|
117
106
|
} catch {
|
|
118
|
-
return null
|
|
107
|
+
return null;
|
|
119
108
|
}
|
|
120
109
|
}
|
|
121
110
|
|
|
122
111
|
function handleReady(state: ClientState, event: StreamEvent): void {
|
|
123
|
-
state.
|
|
124
|
-
resetBackoff(state.backoff)
|
|
125
|
-
setStatus(state, CHANNEL_STATUS_CONNECTED)
|
|
126
|
-
state.options.onReady?.()
|
|
112
|
+
state.connectionId = readConnectionId(event.data);
|
|
113
|
+
resetBackoff(state.backoff);
|
|
114
|
+
setStatus(state, CHANNEL_STATUS_CONNECTED);
|
|
115
|
+
state.options.onReady?.();
|
|
127
116
|
}
|
|
128
117
|
|
|
129
118
|
function handleEvent(state: ClientState, event: StreamEvent): void {
|
|
130
|
-
if (event.name === CHANNEL_EVENT_READY) return handleReady(state, event)
|
|
131
|
-
if (
|
|
132
|
-
state.options.onFrame?.(event.
|
|
119
|
+
if (event.name === CHANNEL_EVENT_READY) return handleReady(state, event);
|
|
120
|
+
if (event.name !== CHANNEL_EVENT_FRAME) return;
|
|
121
|
+
state.options.onFrame?.(event.data);
|
|
133
122
|
}
|
|
134
123
|
|
|
135
124
|
function scheduleRetry(state: ClientState): void {
|
|
136
|
-
if (state.retryTimer !== null) return
|
|
137
|
-
const delay = nextDelay(state.backoff, CHANNEL_RECONNECT_DELAYS_MS)
|
|
125
|
+
if (state.retryTimer !== null) return;
|
|
126
|
+
const delay = nextDelay(state.backoff, CHANNEL_RECONNECT_DELAYS_MS);
|
|
138
127
|
state.retryTimer = setTimeout(() => {
|
|
139
|
-
state.retryTimer = null
|
|
140
|
-
connect(state)
|
|
141
|
-
}, delay)
|
|
128
|
+
state.retryTimer = null;
|
|
129
|
+
connect(state);
|
|
130
|
+
}, delay);
|
|
142
131
|
}
|
|
143
132
|
|
|
144
133
|
function lose(state: ClientState): void {
|
|
145
|
-
state.
|
|
146
|
-
state.queue = []
|
|
147
|
-
if (!state.isStarted) return
|
|
148
|
-
setStatus(state, CHANNEL_STATUS_RECONNECTING)
|
|
149
|
-
scheduleRetry(state)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async function run(
|
|
153
|
-
|
|
134
|
+
state.connectionId = null;
|
|
135
|
+
state.queue = [];
|
|
136
|
+
if (!state.isStarted) return;
|
|
137
|
+
setStatus(state, CHANNEL_STATUS_RECONNECTING);
|
|
138
|
+
scheduleRetry(state);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function run(
|
|
142
|
+
state: ClientState,
|
|
143
|
+
controller: AbortController,
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
const open = state.options.openStream ?? openEventStream;
|
|
154
146
|
try {
|
|
155
|
-
const response = await open(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
)
|
|
159
|
-
if (!response.ok || response.body === null) return
|
|
160
|
-
await readEventStream(response.body, (event) => handleEvent(state, event))
|
|
147
|
+
const response = await open(CHANNEL_EVENTS_PATH, controller.signal);
|
|
148
|
+
if (!response.ok || response.body === null) return;
|
|
149
|
+
await readEventStream(response.body, (event) => handleEvent(state, event));
|
|
161
150
|
} catch {
|
|
162
|
-
return
|
|
151
|
+
return;
|
|
163
152
|
} finally {
|
|
164
|
-
if (!controller.signal.aborted) lose(state)
|
|
153
|
+
if (!controller.signal.aborted) lose(state);
|
|
165
154
|
}
|
|
166
155
|
}
|
|
167
156
|
|
|
168
157
|
function connect(state: ClientState): void {
|
|
169
|
-
if (!state.isStarted) return
|
|
170
|
-
state.stream?.abort()
|
|
171
|
-
state.
|
|
172
|
-
const controller = new AbortController()
|
|
173
|
-
state.stream = controller
|
|
174
|
-
void run(state, controller)
|
|
158
|
+
if (!state.isStarted) return;
|
|
159
|
+
state.stream?.abort();
|
|
160
|
+
state.connectionId = null;
|
|
161
|
+
const controller = new AbortController();
|
|
162
|
+
state.stream = controller;
|
|
163
|
+
void run(state, controller);
|
|
175
164
|
}
|
|
176
165
|
|
|
177
166
|
function restart(state: ClientState): void {
|
|
178
|
-
state.stream?.abort()
|
|
179
|
-
lose(state)
|
|
167
|
+
state.stream?.abort();
|
|
168
|
+
lose(state);
|
|
180
169
|
}
|
|
181
170
|
|
|
182
171
|
async function drain(state: ClientState): Promise<void> {
|
|
183
|
-
if (state.isDraining) return
|
|
184
|
-
state.isDraining = true
|
|
185
|
-
while (state.queue.length > 0 && state.
|
|
186
|
-
const next = state.queue.shift()
|
|
187
|
-
const connectionId = state.connections[next.channel]
|
|
188
|
-
if (connectionId === undefined) continue
|
|
172
|
+
if (state.isDraining) return;
|
|
173
|
+
state.isDraining = true;
|
|
174
|
+
while (state.queue.length > 0 && state.connectionId !== null) {
|
|
175
|
+
const next = state.queue.shift();
|
|
189
176
|
try {
|
|
190
|
-
await state.options.post(channelMessagesPath(connectionId), next
|
|
177
|
+
await state.options.post(channelMessagesPath(state.connectionId), next);
|
|
191
178
|
} catch {
|
|
192
|
-
restart(state)
|
|
179
|
+
restart(state);
|
|
193
180
|
}
|
|
194
181
|
}
|
|
195
|
-
state.isDraining = false
|
|
182
|
+
state.isDraining = false;
|
|
196
183
|
}
|
|
197
184
|
|
|
198
|
-
function send(state: ClientState,
|
|
199
|
-
if (state.
|
|
200
|
-
state.queue.push(
|
|
201
|
-
void drain(state)
|
|
202
|
-
return true
|
|
185
|
+
function send(state: ClientState, msg: unknown): boolean {
|
|
186
|
+
if (state.connectionId === null) return false;
|
|
187
|
+
state.queue.push(msg);
|
|
188
|
+
void drain(state);
|
|
189
|
+
return true;
|
|
203
190
|
}
|
|
204
191
|
|
|
205
192
|
function clearRetry(state: ClientState): void {
|
|
206
|
-
if (state.retryTimer !== null) clearTimeout(state.retryTimer)
|
|
207
|
-
state.retryTimer = null
|
|
193
|
+
if (state.retryTimer !== null) clearTimeout(state.retryTimer);
|
|
194
|
+
state.retryTimer = null;
|
|
208
195
|
}
|
|
209
196
|
|
|
210
197
|
function start(state: ClientState): void {
|
|
211
|
-
if (state.isStarted) return
|
|
212
|
-
state.isStarted = true
|
|
213
|
-
resetBackoff(state.backoff)
|
|
214
|
-
setStatus(state, CHANNEL_STATUS_CONNECTING)
|
|
215
|
-
connect(state)
|
|
198
|
+
if (state.isStarted) return;
|
|
199
|
+
state.isStarted = true;
|
|
200
|
+
resetBackoff(state.backoff);
|
|
201
|
+
setStatus(state, CHANNEL_STATUS_CONNECTING);
|
|
202
|
+
connect(state);
|
|
216
203
|
}
|
|
217
204
|
|
|
218
205
|
function stop(state: ClientState): void {
|
|
219
|
-
if (!state.isStarted) return
|
|
220
|
-
state.isStarted = false
|
|
221
|
-
clearRetry(state)
|
|
222
|
-
state.stream?.abort()
|
|
223
|
-
state.
|
|
224
|
-
state.queue = []
|
|
225
|
-
setStatus(state, CHANNEL_STATUS_DISCONNECTED)
|
|
206
|
+
if (!state.isStarted) return;
|
|
207
|
+
state.isStarted = false;
|
|
208
|
+
clearRetry(state);
|
|
209
|
+
state.stream?.abort();
|
|
210
|
+
state.connectionId = null;
|
|
211
|
+
state.queue = [];
|
|
212
|
+
setStatus(state, CHANNEL_STATUS_DISCONNECTED);
|
|
226
213
|
}
|
|
227
214
|
|
|
228
215
|
function reconnectNow(state: ClientState): void {
|
|
229
|
-
if (!state.isStarted) return
|
|
230
|
-
clearRetry(state)
|
|
231
|
-
resetBackoff(state.backoff)
|
|
232
|
-
setStatus(state, CHANNEL_STATUS_CONNECTING)
|
|
233
|
-
connect(state)
|
|
216
|
+
if (!state.isStarted) return;
|
|
217
|
+
clearRetry(state);
|
|
218
|
+
resetBackoff(state.backoff);
|
|
219
|
+
setStatus(state, CHANNEL_STATUS_CONNECTING);
|
|
220
|
+
connect(state);
|
|
234
221
|
}
|
|
235
222
|
|
|
236
223
|
/**
|
|
237
|
-
*
|
|
238
|
-
* sequential POSTs to send
|
|
239
|
-
* connections per origin, so a tab holds one such stream and
|
|
240
|
-
* needs it: the caller starts and stops it. While started, any
|
|
241
|
-
* stream — a sidecar that went down, a hot reload of dms-ai, a
|
|
242
|
-
* reconnects with backoff, and each new connection is announced
|
|
243
|
-
* `onReady` so the caller identifies itself again.
|
|
224
|
+
* The tab's one stream to the sidecar through the DMS, carrying the dashboard's
|
|
225
|
+
* and the chat's frames alike, and sequential POSTs to send. Browsers keep at
|
|
226
|
+
* most six HTTP/1.1 connections per origin, so a tab holds one such stream and
|
|
227
|
+
* only while it needs it: the caller starts and stops it. While started, any
|
|
228
|
+
* end of the stream — a sidecar that went down, a hot reload of dms-ai, a
|
|
229
|
+
* network drop — reconnects with backoff, and each new connection is announced
|
|
230
|
+
* through `onReady` so the caller identifies itself again.
|
|
244
231
|
*/
|
|
245
|
-
export function createChannelClient(
|
|
232
|
+
export function createChannelClient(
|
|
233
|
+
options: ChannelClientOptions,
|
|
234
|
+
): ChannelClient {
|
|
246
235
|
const state: ClientState = {
|
|
247
236
|
options,
|
|
248
237
|
status: CHANNEL_STATUS_DISCONNECTED,
|
|
249
|
-
|
|
238
|
+
connectionId: null,
|
|
250
239
|
stream: null,
|
|
251
240
|
retryTimer: null,
|
|
252
241
|
queue: [],
|
|
253
242
|
isDraining: false,
|
|
254
243
|
isStarted: false,
|
|
255
244
|
backoff: createBackoff(),
|
|
256
|
-
}
|
|
245
|
+
};
|
|
257
246
|
return {
|
|
258
|
-
send: (
|
|
259
|
-
isConnected: () => state.
|
|
247
|
+
send: (msg) => send(state, msg),
|
|
248
|
+
isConnected: () => state.connectionId !== null,
|
|
260
249
|
getStatus: () => state.status,
|
|
261
250
|
start: () => start(state),
|
|
262
251
|
stop: () => stop(state),
|
|
263
252
|
reconnectNow: () => reconnectNow(state),
|
|
264
|
-
}
|
|
253
|
+
};
|
|
265
254
|
}
|
|
@@ -1,82 +1,52 @@
|
|
|
1
|
-
import type { ChannelStatus } from
|
|
2
|
-
import {
|
|
1
|
+
import type { ChannelStatus } from "./channel-client";
|
|
2
|
+
import { createListeners } from "./listeners";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* lends the chat its channel of it.
|
|
5
|
+
* The chat's side of the tab's stream: the dashboard owns the stream and hands
|
|
6
|
+
* the chat the sidecar messages that are not host commands.
|
|
8
7
|
*/
|
|
9
8
|
export interface ChatTransport {
|
|
10
|
-
/**
|
|
11
|
-
|
|
9
|
+
/** Sidecar messages for the chat, parsed once by the dashboard, in order. */
|
|
10
|
+
onMessage: (listener: (msg: object) => void) => () => void;
|
|
12
11
|
/** Every (re)connection is a new sidecar socket: the chat says hello again. */
|
|
13
|
-
onReady: (listener: () => void) => () => void
|
|
14
|
-
onStatus: (listener: (status: ChannelStatus) => void) => () => void
|
|
15
|
-
getStatus: () => ChannelStatus
|
|
16
|
-
/** False when the message was dropped because the
|
|
17
|
-
send: (msg: unknown) => boolean
|
|
18
|
-
reconnect: () => void
|
|
12
|
+
onReady: (listener: () => void) => () => void;
|
|
13
|
+
onStatus: (listener: (status: ChannelStatus) => void) => () => void;
|
|
14
|
+
getStatus: () => ChannelStatus;
|
|
15
|
+
/** False when the message was dropped because the stream is not connected. */
|
|
16
|
+
send: (msg: unknown) => boolean;
|
|
17
|
+
reconnect: () => void;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export interface ChatTransportActions {
|
|
22
|
-
send: (msg: unknown) => boolean
|
|
23
|
-
reconnect: () => void
|
|
24
|
-
getStatus: () => ChannelStatus
|
|
21
|
+
send: (msg: unknown) => boolean;
|
|
22
|
+
reconnect: () => void;
|
|
23
|
+
getStatus: () => ChannelStatus;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
export interface ChatTransportHub {
|
|
28
|
-
transport: ChatTransport
|
|
29
|
-
|
|
30
|
-
announceReady: () => void
|
|
31
|
-
announceStatus: (status: ChannelStatus) => void
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface Listeners<T> {
|
|
35
|
-
add: (listener: (value: T) => void) => () => void
|
|
36
|
-
emit: (value: T) => void
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function createListeners<T>(): Listeners<T> {
|
|
40
|
-
const listeners = new Set<(value: T) => void>()
|
|
41
|
-
return {
|
|
42
|
-
add: (listener) => {
|
|
43
|
-
listeners.add(listener)
|
|
44
|
-
return () => listeners.delete(listener)
|
|
45
|
-
},
|
|
46
|
-
emit: (value) => {
|
|
47
|
-
for (const listener of listeners) {
|
|
48
|
-
try {
|
|
49
|
-
listener(value)
|
|
50
|
-
} catch (error: unknown) {
|
|
51
|
-
console.error(`${LOG_PREFIX} chat listener failed`, error)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
}
|
|
27
|
+
transport: ChatTransport;
|
|
28
|
+
deliver: (msg: object) => void;
|
|
29
|
+
announceReady: () => void;
|
|
30
|
+
announceStatus: (status: ChannelStatus) => void;
|
|
56
31
|
}
|
|
57
32
|
|
|
58
33
|
export function createChatTransport(
|
|
59
34
|
actions: ChatTransportActions,
|
|
60
35
|
): ChatTransportHub {
|
|
61
|
-
const
|
|
62
|
-
const ready = createListeners<undefined>()
|
|
63
|
-
const statuses = createListeners<ChannelStatus>()
|
|
36
|
+
const messages = createListeners<object>();
|
|
37
|
+
const ready = createListeners<undefined>();
|
|
38
|
+
const statuses = createListeners<ChannelStatus>();
|
|
64
39
|
return {
|
|
65
40
|
transport: {
|
|
66
|
-
|
|
41
|
+
onMessage: messages.add,
|
|
67
42
|
onReady: (listener) => ready.add(() => listener()),
|
|
68
43
|
onStatus: statuses.add,
|
|
69
44
|
getStatus: actions.getStatus,
|
|
70
45
|
send: actions.send,
|
|
71
46
|
reconnect: actions.reconnect,
|
|
72
47
|
},
|
|
73
|
-
|
|
48
|
+
deliver: messages.emit,
|
|
74
49
|
announceReady: () => ready.emit(undefined),
|
|
75
50
|
announceStatus: statuses.emit,
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Publishes the transport where the chat document looks for it. */
|
|
80
|
-
export function exposeChatTransport(transport: ChatTransport): void {
|
|
81
|
-
Reflect.set(globalThis, CHAT_TRANSPORT_KEY, transport)
|
|
51
|
+
};
|
|
82
52
|
}
|
|
@@ -1,81 +1,91 @@
|
|
|
1
|
-
export const OVERLAY_DEFAULT_WIDTH_PX = 460
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export const
|
|
1
|
+
export const OVERLAY_DEFAULT_WIDTH_PX = 460;
|
|
2
|
+
/**
|
|
3
|
+
* Above the dashboard's header and the builder's overlay (z-50), below what the
|
|
4
|
+
* dashboard portals out of the app: the DMS paints its popovers, modals and
|
|
5
|
+
* toasts in an isolated layer after the app, whatever this value is.
|
|
6
|
+
*/
|
|
7
|
+
export const OVERLAY_Z_INDEX = 55;
|
|
8
|
+
export const OVERLAY_DOM_ID = "dms-ai-overlay-root";
|
|
9
|
+
export const DMS_OVERLAYS_DOM_ID = "dms-overlays";
|
|
10
|
+
export const SIDECAR_INFO_PATH = "/ai/sidecar-info";
|
|
8
11
|
/** Event stream to receive, POST to send: HTTP the DMS frontend server relays. */
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
export const CHANNEL_MESSAGES_SEGMENT =
|
|
12
|
-
export const
|
|
13
|
-
|
|
14
|
-
export const
|
|
15
|
-
export const CHANNEL_EVENT_READY = 'ready'
|
|
12
|
+
export const CHANNEL_PATH = "/ai/channel";
|
|
13
|
+
export const CHANNEL_EVENTS_PATH = `${CHANNEL_PATH}/events`;
|
|
14
|
+
export const CHANNEL_MESSAGES_SEGMENT = "messages";
|
|
15
|
+
export const CHANNEL_EVENT_READY = "ready";
|
|
16
|
+
/** A sidecar frame: the stream's default event name. */
|
|
17
|
+
export const CHANNEL_EVENT_FRAME = "message";
|
|
16
18
|
/**
|
|
17
19
|
* How long the stream outlives a closed panel or a hidden tab, so a quick
|
|
18
20
|
* close and reopen does not reconnect, while a tab left in the background soon
|
|
19
21
|
* gives back its one of the browser's six HTTP/1.1 connections to the dashboard.
|
|
20
22
|
*/
|
|
21
|
-
export const CHANNEL_IDLE_STOP_MS = 2_000
|
|
22
|
-
export const VISIBILITY_CHANGE_EVENT =
|
|
23
|
-
|
|
24
|
-
export const
|
|
25
|
-
export const OVERLAY_OPEN_CHANGE_EVENT = 'dms-ai:open-change'
|
|
26
|
-
export const EVENT_STREAM_TYPE = 'text/event-stream'
|
|
27
|
-
export const JSON_CONTENT_TYPE = 'application/json'
|
|
28
|
-
export const OVERLAY_BOX_SHADOW = '0 8px 24px rgba(0, 0, 0, 0.18)'
|
|
23
|
+
export const CHANNEL_IDLE_STOP_MS = 2_000;
|
|
24
|
+
export const VISIBILITY_CHANGE_EVENT = "visibilitychange";
|
|
25
|
+
export const EVENT_STREAM_TYPE = "text/event-stream";
|
|
26
|
+
export const JSON_CONTENT_TYPE = "application/json";
|
|
29
27
|
export const CHANNEL_RECONNECT_DELAYS_MS = [
|
|
30
28
|
1000, 2000, 4000, 8000, 15000, 30000,
|
|
31
|
-
] as const
|
|
29
|
+
] as const;
|
|
32
30
|
// Sidecar reachability, as seen by the host. `reviving` is the self-healing
|
|
33
31
|
// state (idle-exited / crashed-respawning): the probe loop revives the sidecar
|
|
34
32
|
// and the channels reconnect once it answers. `unavailable` is terminal (the
|
|
35
33
|
// crash budget is spent) and needs a reload.
|
|
36
|
-
export const SIDECAR_STATUS_CONNECTING =
|
|
37
|
-
export const SIDECAR_STATUS_CONNECTED =
|
|
38
|
-
export const SIDECAR_STATUS_REVIVING =
|
|
39
|
-
export const SIDECAR_STATUS_UNAVAILABLE =
|
|
34
|
+
export const SIDECAR_STATUS_CONNECTING = "connecting";
|
|
35
|
+
export const SIDECAR_STATUS_CONNECTED = "connected";
|
|
36
|
+
export const SIDECAR_STATUS_REVIVING = "reviving";
|
|
37
|
+
export const SIDECAR_STATUS_UNAVAILABLE = "unavailable";
|
|
40
38
|
// Re-probe cadence while reviving. Each probe also revives the daemon backend
|
|
41
39
|
// side, so this doubles as the respawn heartbeat; capped so a genuinely stuck
|
|
42
40
|
// sidecar is polled sparingly rather than hammered.
|
|
43
|
-
export const SIDECAR_POLL_DELAYS_MS = [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export const
|
|
47
|
-
export const
|
|
48
|
-
export const PLACEHOLDER_REVIVING_TEXT = 'Reconnecting the assistant…'
|
|
49
|
-
export const PLACEHOLDER_UNAVAILABLE_TITLE = 'Assistant unavailable'
|
|
50
|
-
export const PLACEHOLDER_UNAVAILABLE_TEXT = 'Reload the page to try again.'
|
|
41
|
+
export const SIDECAR_POLL_DELAYS_MS = [500, 1000, 2000, 4000, 8000] as const;
|
|
42
|
+
export const PLACEHOLDER_CONNECTING_TEXT = "Connecting the assistant…";
|
|
43
|
+
export const PLACEHOLDER_REVIVING_TEXT = "Reconnecting the assistant…";
|
|
44
|
+
export const PLACEHOLDER_UNAVAILABLE_TITLE = "Assistant unavailable";
|
|
45
|
+
export const PLACEHOLDER_UNAVAILABLE_TEXT = "Reload the page to try again.";
|
|
51
46
|
|
|
52
|
-
export const CHANNEL_STATUS_CONNECTING =
|
|
53
|
-
export const CHANNEL_STATUS_CONNECTED =
|
|
54
|
-
export const CHANNEL_STATUS_DISCONNECTED =
|
|
55
|
-
export const CHANNEL_STATUS_RECONNECTING =
|
|
56
|
-
export const HOST_ROLE =
|
|
57
|
-
export const HELLO_MESSAGE_TYPE =
|
|
58
|
-
export const LOG_PREFIX =
|
|
59
|
-
export const HOST_STATE_UPDATE_TYPE =
|
|
60
|
-
export const HOST_NAVIGATION_COMPLETE_TYPE =
|
|
61
|
-
export const HOST_COMMAND_NAVIGATE_TYPE =
|
|
62
|
-
|
|
63
|
-
export const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
export const
|
|
67
|
-
export const
|
|
68
|
-
export const
|
|
47
|
+
export const CHANNEL_STATUS_CONNECTING = "connecting";
|
|
48
|
+
export const CHANNEL_STATUS_CONNECTED = "connected";
|
|
49
|
+
export const CHANNEL_STATUS_DISCONNECTED = "disconnected";
|
|
50
|
+
export const CHANNEL_STATUS_RECONNECTING = "reconnecting";
|
|
51
|
+
export const HOST_ROLE = "host";
|
|
52
|
+
export const HELLO_MESSAGE_TYPE = "hello";
|
|
53
|
+
export const LOG_PREFIX = "[dms-ai]";
|
|
54
|
+
export const HOST_STATE_UPDATE_TYPE = "host_state_update";
|
|
55
|
+
export const HOST_NAVIGATION_COMPLETE_TYPE = "host_navigation_complete";
|
|
56
|
+
export const HOST_COMMAND_NAVIGATE_TYPE = "host_command_navigate";
|
|
57
|
+
/** Frames of the tab's stream meant for the dashboard; every other one is the chat's. */
|
|
58
|
+
export const HOST_COMMAND_TYPES: ReadonlySet<string> = new Set([
|
|
59
|
+
HOST_COMMAND_NAVIGATE_TYPE,
|
|
60
|
+
]);
|
|
61
|
+
export const OVERLAY_MIN_WIDTH_PX = 320;
|
|
62
|
+
export const OVERLAY_PREFS_STORAGE_KEY = "dms-ai:overlay-prefs";
|
|
63
|
+
export const OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS = 300;
|
|
64
|
+
export const OVERLAY_PREFS_DEBOUNCE_MS = 300;
|
|
65
|
+
export const TOGGLE_SHORTCUT_KEY = "k";
|
|
66
|
+
export const MODAL_OPEN_SELECTOR = '[role="dialog"][aria-modal="true"]';
|
|
69
67
|
|
|
70
68
|
// Generic header-action registry exposed by the DMS core (dms-back). Any module
|
|
71
69
|
// can push a button by writing to this shared `useDmsState` key; the core renders
|
|
72
70
|
// it with no knowledge of who registered it.
|
|
73
|
-
export const HEADER_ACTIONS_STATE_KEY =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
export const HEADER_ACTIONS_STATE_KEY = "dms:header-actions";
|
|
72
|
+
/**
|
|
73
|
+
* The DMS's persistent overlays: components rendered on every page, inside the
|
|
74
|
+
* app but outside the routed page, so they live through Inertia navigations.
|
|
75
|
+
* The state key and the component name are the whole contract.
|
|
76
|
+
*/
|
|
77
|
+
export const APP_OVERLAYS_STATE_KEY = "dms-app-overlays";
|
|
78
|
+
export const CHAT_PANEL_COMPONENT_NAME = "DmsAiChatPanel";
|
|
79
|
+
export const LAUNCHER_ACTION_ID = "dms-ai-launcher";
|
|
80
|
+
/**
|
|
81
|
+
* The chat panel's header shows the same icon, which is what gets it into the
|
|
82
|
+
* renderer's icon bundle: it only scans `.vue` files for icon names.
|
|
83
|
+
*/
|
|
84
|
+
export const LAUNCHER_ICON = "i-ph-robot";
|
|
85
|
+
export const LAUNCHER_LABEL = "AI assistant";
|
|
86
|
+
export const PANEL_LABEL = LAUNCHER_LABEL;
|
|
87
|
+
export const LAUNCHER_ORDER = 50;
|
|
78
88
|
// Toggle shortcut, shown in the launcher tooltip. The combo is meta/ctrl + shift
|
|
79
89
|
// + k, so render it the way each platform expects.
|
|
80
|
-
export const TOGGLE_SHORTCUT_LABEL_MAC =
|
|
81
|
-
export const TOGGLE_SHORTCUT_LABEL_OTHER =
|
|
90
|
+
export const TOGGLE_SHORTCUT_LABEL_MAC = "⌘⇧K";
|
|
91
|
+
export const TOGGLE_SHORTCUT_LABEL_OTHER = "Ctrl+Shift+K";
|