@agent-native/dispatch 0.38.15 → 0.38.16-nightly-20260927213438
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/dist/actions/create-workspace-resource.d.ts +13 -13
- package/dist/actions/provider-api-register.d.ts +12 -12
- package/dist/components/automation-create-dialog.js +4 -4
- package/dist/components/automation-create-dialog.js.map +1 -1
- package/dist/components/layout/Header.js +1 -1
- package/dist/components/layout/Header.js.map +1 -1
- package/dist/components/layout/Layout.d.ts +7 -0
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +19 -4
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/messaging-setup-panel.d.ts.map +1 -1
- package/dist/components/messaging-setup-panel.js +7 -28
- package/dist/components/messaging-setup-panel.js.map +1 -1
- package/dist/components/transactional-email-send-log.js +1 -1
- package/dist/components/transactional-email-send-log.js.map +1 -1
- package/dist/components/ui/alert-dialog.d.ts +1 -20
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.js +1 -26
- package/dist/components/ui/alert-dialog.js.map +1 -1
- package/dist/components/ui/button.d.ts +1 -11
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +1 -33
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/carousel.d.ts +2 -2
- package/dist/components/ui/carousel.d.ts.map +1 -1
- package/dist/components/ui/input.d.ts +1 -3
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/input.js +1 -8
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/sidebar.d.ts +4 -2
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/components/ui/switch.d.ts +1 -4
- package/dist/components/ui/switch.d.ts.map +1 -1
- package/dist/components/ui/switch.js +1 -7
- package/dist/components/ui/switch.js.map +1 -1
- package/dist/components/ui/tabs.d.ts +1 -7
- package/dist/components/ui/tabs.d.ts.map +1 -1
- package/dist/components/ui/tabs.js +1 -12
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/components/ui/toggle.d.ts +1 -12
- package/dist/components/ui/toggle.d.ts.map +1 -1
- package/dist/components/ui/toggle.js +1 -25
- package/dist/components/ui/toggle.js.map +1 -1
- package/dist/components/usage-alerts-panel.d.ts.map +1 -1
- package/dist/components/usage-alerts-panel.js +1 -1
- package/dist/components/usage-alerts-panel.js.map +1 -1
- package/dist/components/workspace-app-search.d.ts.map +1 -1
- package/dist/components/workspace-app-search.js +1 -1
- package/dist/components/workspace-app-search.js.map +1 -1
- package/dist/routes/pages/automations.d.ts.map +1 -1
- package/dist/routes/pages/automations.js +2 -2
- package/dist/routes/pages/automations.js.map +1 -1
- package/dist/routes/pages/metrics.d.ts.map +1 -1
- package/dist/routes/pages/metrics.js +1 -1
- package/dist/routes/pages/metrics.js.map +1 -1
- package/dist/routes/pages/settings.d.ts +9 -0
- package/dist/routes/pages/settings.d.ts.map +1 -1
- package/dist/routes/pages/settings.js +65 -12
- package/dist/routes/pages/settings.js.map +1 -1
- package/dist/routes/pages/thread-debug.js +1 -1
- package/dist/routes/pages/thread-debug.js.map +1 -1
- package/dist/routes/pages/vault.d.ts.map +1 -1
- package/dist/routes/pages/vault.js +2 -2
- package/dist/routes/pages/vault.js.map +1 -1
- package/dist/server/lib/usage-metrics-store.js +2 -3
- package/dist/server/lib/usage-metrics-store.js.map +1 -1
- package/dist/server/plugins/db.d.ts.map +1 -1
- package/dist/server/plugins/db.js +44 -0
- package/dist/server/plugins/db.js.map +1 -1
- package/package.json +4 -4
- package/src/components/automation-create-dialog.tsx +6 -6
- package/src/components/layout/Header.tsx +2 -2
- package/src/components/layout/Layout.spec.tsx +28 -0
- package/src/components/layout/Layout.tsx +73 -43
- package/src/components/messaging-setup-panel.spec.tsx +5 -1
- package/src/components/messaging-setup-panel.tsx +9 -45
- package/src/components/transactional-email-send-log.tsx +1 -1
- package/src/components/ui/alert-dialog.tsx +1 -139
- package/src/components/ui/button.tsx +1 -56
- package/src/components/ui/input.tsx +1 -22
- package/src/components/ui/switch.tsx +1 -27
- package/src/components/ui/tabs.tsx +1 -53
- package/src/components/ui/toggle.tsx +1 -43
- package/src/components/usage-alerts-panel.tsx +6 -12
- package/src/components/workspace-app-search.tsx +2 -1
- package/src/routes/pages/automations.tsx +4 -2
- package/src/routes/pages/metrics.tsx +2 -1
- package/src/routes/pages/settings.spec.tsx +179 -0
- package/src/routes/pages/settings.tsx +179 -119
- package/src/routes/pages/thread-debug.tsx +1 -1
- package/src/routes/pages/vault.tsx +3 -2
- package/src/server/lib/usage-metrics-store.spec.ts +1 -0
- package/src/server/lib/usage-metrics-store.ts +2 -2
- package/src/server/plugins/db.ts +49 -0
|
@@ -124,7 +124,7 @@ function AlertEditor({
|
|
|
124
124
|
}
|
|
125
125
|
disabled={isExisting}
|
|
126
126
|
>
|
|
127
|
-
<SelectTrigger className="
|
|
127
|
+
<SelectTrigger className="text-sm">
|
|
128
128
|
<SelectValue />
|
|
129
129
|
</SelectTrigger>
|
|
130
130
|
<SelectContent>
|
|
@@ -143,7 +143,7 @@ function AlertEditor({
|
|
|
143
143
|
value={draft.unit}
|
|
144
144
|
onValueChange={(value) => onChange({ unit: value as AlertUnit })}
|
|
145
145
|
>
|
|
146
|
-
<SelectTrigger className="
|
|
146
|
+
<SelectTrigger className="text-sm">
|
|
147
147
|
<SelectValue />
|
|
148
148
|
</SelectTrigger>
|
|
149
149
|
<SelectContent>
|
|
@@ -161,7 +161,7 @@ function AlertEditor({
|
|
|
161
161
|
step={draft.unit === "tokens" ? "1" : "0.01"}
|
|
162
162
|
value={draft.limit}
|
|
163
163
|
onChange={(event) => onChange({ limit: event.target.value })}
|
|
164
|
-
className="
|
|
164
|
+
className="text-sm"
|
|
165
165
|
/>
|
|
166
166
|
</label>
|
|
167
167
|
<label className="space-y-1.5 text-xs text-muted-foreground">
|
|
@@ -172,7 +172,7 @@ function AlertEditor({
|
|
|
172
172
|
onChange({ period: value as "day" | "month" })
|
|
173
173
|
}
|
|
174
174
|
>
|
|
175
|
-
<SelectTrigger className="
|
|
175
|
+
<SelectTrigger className="text-sm">
|
|
176
176
|
<SelectValue />
|
|
177
177
|
</SelectTrigger>
|
|
178
178
|
<SelectContent>
|
|
@@ -199,19 +199,13 @@ function AlertEditor({
|
|
|
199
199
|
<Button
|
|
200
200
|
type="button"
|
|
201
201
|
size="sm"
|
|
202
|
-
className="ms-auto
|
|
202
|
+
className="ms-auto"
|
|
203
203
|
onClick={onSave}
|
|
204
204
|
disabled={isPending}
|
|
205
205
|
>
|
|
206
206
|
Save
|
|
207
207
|
</Button>
|
|
208
|
-
<Button
|
|
209
|
-
type="button"
|
|
210
|
-
variant="ghost"
|
|
211
|
-
size="sm"
|
|
212
|
-
className="h-8"
|
|
213
|
-
onClick={onCancel}
|
|
214
|
-
>
|
|
208
|
+
<Button type="button" variant="ghost" size="sm" onClick={onCancel}>
|
|
215
209
|
Cancel
|
|
216
210
|
</Button>
|
|
217
211
|
</div>
|
|
@@ -37,10 +37,11 @@ export function WorkspaceAppSearch({
|
|
|
37
37
|
/>
|
|
38
38
|
<Input
|
|
39
39
|
id={inputId}
|
|
40
|
+
size="sm"
|
|
40
41
|
value={query}
|
|
41
42
|
onChange={(event) => onQueryChange(event.target.value)}
|
|
42
43
|
placeholder={placeholder}
|
|
43
|
-
className="
|
|
44
|
+
className="bg-background ps-9"
|
|
44
45
|
autoComplete="off"
|
|
45
46
|
/>
|
|
46
47
|
</div>
|
|
@@ -228,11 +228,12 @@ export default function AutomationsRoute() {
|
|
|
228
228
|
className="pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground"
|
|
229
229
|
/>
|
|
230
230
|
<Input
|
|
231
|
+
size="sm"
|
|
231
232
|
value={query}
|
|
232
233
|
onChange={(event) => setQuery(event.target.value)}
|
|
233
234
|
placeholder="Search automations"
|
|
234
235
|
aria-label="Search automations"
|
|
235
|
-
className="
|
|
236
|
+
className="pl-8 text-xs"
|
|
236
237
|
/>
|
|
237
238
|
</div>
|
|
238
239
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
@@ -251,7 +252,8 @@ export default function AutomationsRoute() {
|
|
|
251
252
|
}}
|
|
252
253
|
>
|
|
253
254
|
<SelectTrigger
|
|
254
|
-
|
|
255
|
+
size="sm"
|
|
256
|
+
className="w-[10.5rem] text-xs"
|
|
255
257
|
aria-label="Automation view"
|
|
256
258
|
>
|
|
257
259
|
<SelectValue />
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { act, type ComponentType, type ReactNode } from "react";
|
|
4
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
5
|
+
import { MemoryRouter } from "react-router";
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
import { dispatchAccessDescriptor } from "../../shared/app-roles.js";
|
|
9
|
+
|
|
10
|
+
const state = vi.hoisted(() => ({
|
|
11
|
+
redesign: false,
|
|
12
|
+
registered: [] as Array<{
|
|
13
|
+
id: string;
|
|
14
|
+
component: ComponentType;
|
|
15
|
+
searchEntries?: unknown;
|
|
16
|
+
}>,
|
|
17
|
+
membersAppRoles: undefined as unknown,
|
|
18
|
+
pageProps: null as null | {
|
|
19
|
+
general?: ReactNode;
|
|
20
|
+
generalGroups?: ReactNode;
|
|
21
|
+
generalSearchEntries?: Array<{ id: string; hash?: string }>;
|
|
22
|
+
extraTabs?: Array<{ id: string; href?: string }>;
|
|
23
|
+
whatsNew?: ReactNode;
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
vi.mock("@agent-native/core/client/agent-chat", () => ({
|
|
28
|
+
CHAT_FIRST_MODE_CHANGED_EVENT: "chat-first-mode-changed",
|
|
29
|
+
readChatFirstModeState: () => ({ enabled: false, availability: "ok" }),
|
|
30
|
+
writeChatFirstMode: () => ({ ok: true }),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
vi.mock("@agent-native/core/client/changelog", () => ({
|
|
34
|
+
ChangelogSettingsCard: ({ markdown }: { markdown: string }) => (
|
|
35
|
+
<div data-changelog>{markdown}</div>
|
|
36
|
+
),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
vi.mock("@agent-native/core/client/feature-flags", () => ({
|
|
40
|
+
useFeatureFlag: () => false,
|
|
41
|
+
useFeatureFlagState: () => ({ status: "ready", enabled: state.redesign }),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
vi.mock("@agent-native/core/feature-flags/registry", () => ({
|
|
45
|
+
CONNECT_APPS_FLAG: { key: "connect-apps" },
|
|
46
|
+
SETTINGS_REDESIGN_FLAG: { key: "settings-redesign" },
|
|
47
|
+
defineFeatureFlag: (flag: { key: string }) => flag,
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
vi.mock("@agent-native/core/client/i18n", () => ({
|
|
51
|
+
useT: () => (key: string) => key,
|
|
52
|
+
LanguagePicker: () => null,
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
vi.mock("@agent-native/core/client/org", () => ({
|
|
56
|
+
TeamPage: () => null,
|
|
57
|
+
OrgMembersPage: ({ appRoles }: { appRoles?: unknown }) => {
|
|
58
|
+
state.membersAppRoles = appRoles;
|
|
59
|
+
return <div data-members-page />;
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
vi.mock("@agent-native/core/client/settings", () => ({
|
|
64
|
+
AccountSettingsCard: () => null,
|
|
65
|
+
CORE_SETTINGS_PAGES: [
|
|
66
|
+
{ id: "profile", component: () => null },
|
|
67
|
+
{
|
|
68
|
+
id: "members",
|
|
69
|
+
component: () => null,
|
|
70
|
+
searchEntries: [{ id: "invite-members" }],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
registerSettingsPages: (pages: typeof state.registered) => {
|
|
74
|
+
state.registered.push(...pages);
|
|
75
|
+
},
|
|
76
|
+
SettingsGroup: ({ children }: { children: ReactNode }) => (
|
|
77
|
+
<section>{children}</section>
|
|
78
|
+
),
|
|
79
|
+
SettingsRow: ({
|
|
80
|
+
id,
|
|
81
|
+
label,
|
|
82
|
+
children,
|
|
83
|
+
}: {
|
|
84
|
+
id?: string;
|
|
85
|
+
label: ReactNode;
|
|
86
|
+
children?: ReactNode;
|
|
87
|
+
}) => (
|
|
88
|
+
<div data-row={id}>
|
|
89
|
+
{label}
|
|
90
|
+
{children}
|
|
91
|
+
</div>
|
|
92
|
+
),
|
|
93
|
+
SettingsTabsPage: (props: NonNullable<typeof state.pageProps>) => {
|
|
94
|
+
state.pageProps = props;
|
|
95
|
+
return <main>{state.redesign ? props.generalGroups : props.general}</main>;
|
|
96
|
+
},
|
|
97
|
+
useAgentSettingsTabs: () => [{ id: "organization", label: "Organization" }],
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
const { DispatchSettingsPage } = await import("./settings.js");
|
|
101
|
+
|
|
102
|
+
describe("Dispatch settings route", () => {
|
|
103
|
+
let container: HTMLDivElement;
|
|
104
|
+
let root: Root;
|
|
105
|
+
|
|
106
|
+
function render() {
|
|
107
|
+
act(() => {
|
|
108
|
+
root.render(
|
|
109
|
+
<MemoryRouter>
|
|
110
|
+
<DispatchSettingsPage changelog="## 2026-09-25" />
|
|
111
|
+
</MemoryRouter>,
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
118
|
+
state.redesign = false;
|
|
119
|
+
state.pageProps = null;
|
|
120
|
+
state.membersAppRoles = undefined;
|
|
121
|
+
container = document.createElement("div");
|
|
122
|
+
document.body.appendChild(container);
|
|
123
|
+
root = createRoot(container);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
afterEach(() => {
|
|
127
|
+
act(() => root.unmount());
|
|
128
|
+
container.remove();
|
|
129
|
+
vi.unstubAllGlobals();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("replaces core Members with one that keeps Dispatch app roles", () => {
|
|
133
|
+
const members = state.registered.find((page) => page.id === "members");
|
|
134
|
+
expect(members?.searchEntries).toEqual([{ id: "invite-members" }]);
|
|
135
|
+
|
|
136
|
+
const Members = members!.component;
|
|
137
|
+
act(() => {
|
|
138
|
+
root.render(<Members />);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(container.querySelector("[data-members-page]")).not.toBeNull();
|
|
142
|
+
expect(state.membersAppRoles).toBe(dispatchAccessDescriptor);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("keeps the language row and Admin link on today's General tab", () => {
|
|
146
|
+
render();
|
|
147
|
+
|
|
148
|
+
expect(container.querySelector('[data-row="language"]')).not.toBeNull();
|
|
149
|
+
expect(container.textContent).toContain(
|
|
150
|
+
"settings.chatFirstSessionWatchDescription",
|
|
151
|
+
);
|
|
152
|
+
expect(
|
|
153
|
+
state.pageProps?.generalSearchEntries?.map((entry) => entry.id),
|
|
154
|
+
).toContain("dispatch-language");
|
|
155
|
+
expect(
|
|
156
|
+
state.pageProps?.extraTabs?.find((tab) => tab.id === "admin")?.href,
|
|
157
|
+
).toBe("/admin");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("drops the language row in the redesigned Settings", () => {
|
|
161
|
+
state.redesign = true;
|
|
162
|
+
render();
|
|
163
|
+
|
|
164
|
+
expect(container.querySelector('[data-row="language"]')).toBeNull();
|
|
165
|
+
expect(container.querySelector('[data-row="chat-first"]')).not.toBeNull();
|
|
166
|
+
expect(
|
|
167
|
+
container.querySelector('[data-row="workspace-resources"]')?.textContent,
|
|
168
|
+
).toContain("settings.resourcesTitle");
|
|
169
|
+
expect(container.textContent).not.toContain(
|
|
170
|
+
"settings.chatFirstSessionWatchDescription",
|
|
171
|
+
);
|
|
172
|
+
expect(
|
|
173
|
+
state.pageProps?.generalSearchEntries?.map((entry) => entry.hash),
|
|
174
|
+
).toEqual(["workspace-resources", "chat-first"]);
|
|
175
|
+
expect(
|
|
176
|
+
state.pageProps?.extraTabs?.find((tab) => tab.id === "admin")?.href,
|
|
177
|
+
).toBe("/admin");
|
|
178
|
+
});
|
|
179
|
+
});
|
|
@@ -4,36 +4,69 @@ import {
|
|
|
4
4
|
writeChatFirstMode,
|
|
5
5
|
} from "@agent-native/core/client/agent-chat";
|
|
6
6
|
import { ChangelogSettingsCard } from "@agent-native/core/client/changelog";
|
|
7
|
+
import {
|
|
8
|
+
useFeatureFlag,
|
|
9
|
+
useFeatureFlagState,
|
|
10
|
+
} from "@agent-native/core/client/feature-flags";
|
|
7
11
|
import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
|
|
8
|
-
import { TeamPage } from "@agent-native/core/client/org";
|
|
12
|
+
import { OrgMembersPage, TeamPage } from "@agent-native/core/client/org";
|
|
9
13
|
import {
|
|
14
|
+
AccountSettingsCard,
|
|
15
|
+
CORE_SETTINGS_PAGES,
|
|
16
|
+
registerSettingsPages,
|
|
17
|
+
SettingsGroup,
|
|
18
|
+
SettingsRow,
|
|
10
19
|
SettingsTabsPage,
|
|
11
20
|
useAgentSettingsTabs,
|
|
21
|
+
type SettingsSearchEntry,
|
|
12
22
|
} from "@agent-native/core/client/settings";
|
|
23
|
+
import { SETTINGS_REDESIGN_FLAG } from "@agent-native/core/feature-flags/registry";
|
|
13
24
|
import { IconShield } from "@tabler/icons-react";
|
|
14
|
-
import { useState } from "react";
|
|
25
|
+
import { useMemo, useState } from "react";
|
|
15
26
|
import { Link } from "react-router";
|
|
16
27
|
|
|
17
|
-
import
|
|
18
|
-
import { DispatchShell } from "../../components/dispatch-shell";
|
|
28
|
+
import packageChangelog from "../../../CHANGELOG.md?raw";
|
|
19
29
|
import { Button } from "../../components/ui/button";
|
|
20
|
-
import {
|
|
21
|
-
Card,
|
|
22
|
-
CardContent,
|
|
23
|
-
CardDescription,
|
|
24
|
-
CardHeader,
|
|
25
|
-
CardTitle,
|
|
26
|
-
} from "../../components/ui/card";
|
|
27
|
-
import { Label } from "../../components/ui/label";
|
|
28
30
|
import { Switch } from "../../components/ui/switch";
|
|
29
31
|
import { dispatchAccessDescriptor } from "../../shared/app-roles.js";
|
|
32
|
+
import { DISPATCH_CONNECT_APPS_FLAG } from "../../shared/feature-flags.js";
|
|
33
|
+
|
|
34
|
+
const coreMembersPage = CORE_SETTINGS_PAGES.find(
|
|
35
|
+
(page) => page.id === "members",
|
|
36
|
+
);
|
|
37
|
+
if (!coreMembersPage) {
|
|
38
|
+
throw new Error("Core Settings has no members page for Dispatch app roles");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function DispatchMembersSettingsPage() {
|
|
42
|
+
return <OrgMembersPage appRoles={dispatchAccessDescriptor} />;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The redesigned Settings shows Members from core; Dispatch's replaces it so
|
|
46
|
+
// the app-role column stays.
|
|
47
|
+
registerSettingsPages([
|
|
48
|
+
{ ...coreMembersPage, component: DispatchMembersSettingsPage },
|
|
49
|
+
]);
|
|
30
50
|
|
|
31
51
|
export function meta() {
|
|
32
52
|
return [{ title: "Settings - Dispatch" }];
|
|
33
53
|
}
|
|
34
54
|
|
|
35
|
-
export
|
|
55
|
+
export interface DispatchSettingsPageProps {
|
|
56
|
+
/** Raw CHANGELOG.md behind What's new. */
|
|
57
|
+
changelog: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Dispatch's Settings. The template route renders it with the app's own
|
|
62
|
+
* changelog; `dispatchRoutes` consumers get the default export.
|
|
63
|
+
*/
|
|
64
|
+
export function DispatchSettingsPage({ changelog }: DispatchSettingsPageProps) {
|
|
36
65
|
const t = useT();
|
|
66
|
+
const connectAppsEnabled = useFeatureFlag(DISPATCH_CONNECT_APPS_FLAG.key);
|
|
67
|
+
// Core Preferences owns the interface language in the redesigned Settings,
|
|
68
|
+
// whose Dispatch General page shows `generalGroups` instead of `general`.
|
|
69
|
+
const redesign = useFeatureFlagState(SETTINGS_REDESIGN_FLAG.key).enabled;
|
|
37
70
|
const agentSettingsTabs = useAgentSettingsTabs({
|
|
38
71
|
usageAppId: "dispatch",
|
|
39
72
|
usageViewAllHref: "/admin/metrics",
|
|
@@ -48,7 +81,7 @@ export default function SettingsRoute() {
|
|
|
48
81
|
),
|
|
49
82
|
});
|
|
50
83
|
const settingsTabs = [
|
|
51
|
-
...agentSettingsTabs
|
|
84
|
+
...agentSettingsTabs,
|
|
52
85
|
{
|
|
53
86
|
id: "admin",
|
|
54
87
|
label: t("dispatch.nav.admin", { defaultValue: "Admin" }),
|
|
@@ -66,16 +99,14 @@ export default function SettingsRoute() {
|
|
|
66
99
|
string | null
|
|
67
100
|
>(
|
|
68
101
|
chatFirstModeState.availability === "unavailable"
|
|
69
|
-
? "
|
|
102
|
+
? t("settings.chatFirstStorageUnavailable")
|
|
70
103
|
: null,
|
|
71
104
|
);
|
|
72
105
|
|
|
73
106
|
function updateChatFirstMode(enabled: boolean) {
|
|
74
107
|
const result = writeChatFirstMode(enabled);
|
|
75
108
|
if (!result.ok) {
|
|
76
|
-
setChatFirstStorageNotice(
|
|
77
|
-
"This browser blocked local preferences, so Chat-first mode was not changed.",
|
|
78
|
-
);
|
|
109
|
+
setChatFirstStorageNotice(t("settings.chatFirstStorageBlocked"));
|
|
79
110
|
return;
|
|
80
111
|
}
|
|
81
112
|
setChatFirstStorageNotice(null);
|
|
@@ -87,110 +118,139 @@ export default function SettingsRoute() {
|
|
|
87
118
|
);
|
|
88
119
|
}
|
|
89
120
|
|
|
121
|
+
const generalSearchEntries = useMemo<SettingsSearchEntry[]>(
|
|
122
|
+
() => [
|
|
123
|
+
...(redesign
|
|
124
|
+
? []
|
|
125
|
+
: [
|
|
126
|
+
{
|
|
127
|
+
id: "dispatch-language",
|
|
128
|
+
label: t("settings.languageTitle"),
|
|
129
|
+
keywords: "language locale translation i18n",
|
|
130
|
+
hash: "language",
|
|
131
|
+
},
|
|
132
|
+
]),
|
|
133
|
+
{
|
|
134
|
+
id: "dispatch-workspace",
|
|
135
|
+
label: redesign
|
|
136
|
+
? t("settings.resourcesTitle")
|
|
137
|
+
: t("settings.workspaceTitle"),
|
|
138
|
+
keywords: "workspace resources integrations vault destinations",
|
|
139
|
+
hash: "workspace-resources",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "dispatch-chat-first",
|
|
143
|
+
label: t("settings.chatFirstTitle"),
|
|
144
|
+
keywords: "chat first codex t3 apps pane navigation",
|
|
145
|
+
hash: "chat-first",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
[redesign, t],
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const chatFirstSwitch = (
|
|
152
|
+
<Switch
|
|
153
|
+
aria-label={t("settings.chatFirstAriaLabel")}
|
|
154
|
+
checked={chatFirstMode}
|
|
155
|
+
onCheckedChange={updateChatFirstMode}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
const chatFirstStorageAlert = chatFirstStorageNotice ? (
|
|
159
|
+
<p className="text-sm text-destructive" role="alert">
|
|
160
|
+
{chatFirstStorageNotice}
|
|
161
|
+
</p>
|
|
162
|
+
) : null;
|
|
163
|
+
const connectAppsRow = connectAppsEnabled ? (
|
|
164
|
+
<SettingsRow
|
|
165
|
+
id="connect-apps"
|
|
166
|
+
label={t("settings.connectApps")}
|
|
167
|
+
description={t("settings.connectAppsDescription")}
|
|
168
|
+
control={
|
|
169
|
+
<Button variant="outline" asChild>
|
|
170
|
+
<Link to="/connect">{t("settings.openConnectApps")}</Link>
|
|
171
|
+
</Button>
|
|
172
|
+
}
|
|
173
|
+
/>
|
|
174
|
+
) : null;
|
|
175
|
+
const resourceSettingsButton = (
|
|
176
|
+
<Button variant="outline" asChild>
|
|
177
|
+
<Link to="/workspace">{t("settings.openResourceSettings")}</Link>
|
|
178
|
+
</Button>
|
|
179
|
+
);
|
|
180
|
+
|
|
90
181
|
return (
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<CardHeader>
|
|
101
|
-
<CardTitle className="text-base">
|
|
102
|
-
{t("settings.languageTitle")}
|
|
103
|
-
</CardTitle>
|
|
104
|
-
<CardDescription>
|
|
105
|
-
{t("settings.languageDescription")}
|
|
106
|
-
</CardDescription>
|
|
107
|
-
</CardHeader>
|
|
108
|
-
<CardContent className="max-w-xs space-y-1.5">
|
|
109
|
-
<Label>{t("settings.languageLabel")}</Label>
|
|
110
|
-
<LanguagePicker label={t("settings.languageLabel")} />
|
|
111
|
-
</CardContent>
|
|
112
|
-
</Card>
|
|
182
|
+
<SettingsTabsPage
|
|
183
|
+
account={<AccountSettingsCard />}
|
|
184
|
+
extraTabs={settingsTabs}
|
|
185
|
+
generalSearchEntries={generalSearchEntries}
|
|
186
|
+
general={
|
|
187
|
+
<div className="mx-auto w-full max-w-2xl space-y-6">
|
|
188
|
+
<p className="text-sm leading-6 text-muted-foreground">
|
|
189
|
+
{t("settings.description")}
|
|
190
|
+
</p>
|
|
113
191
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
</CardDescription>
|
|
123
|
-
</CardHeader>
|
|
124
|
-
<CardContent className="flex items-center justify-between gap-4">
|
|
125
|
-
<div className="space-y-1">
|
|
126
|
-
<Label htmlFor="dispatch-chat-first-mode">
|
|
127
|
-
Use chat-first navigation
|
|
128
|
-
</Label>
|
|
129
|
-
<p className="text-sm text-muted-foreground">
|
|
130
|
-
This preference only changes your local Dispatch shell.
|
|
131
|
-
</p>
|
|
132
|
-
<p className="text-sm text-muted-foreground">
|
|
133
|
-
Session watch and follow-up messaging work in this browser
|
|
134
|
-
pane too. Local CLI subscription detection stays in the
|
|
135
|
-
Electron app; Dispatch uses workspace/provider credentials.
|
|
136
|
-
</p>
|
|
192
|
+
<SettingsGroup>
|
|
193
|
+
<SettingsRow
|
|
194
|
+
id="language"
|
|
195
|
+
label={t("settings.languageTitle")}
|
|
196
|
+
description={t("settings.languageDescription")}
|
|
197
|
+
control={
|
|
198
|
+
<div className="w-56">
|
|
199
|
+
<LanguagePicker label={t("settings.languageLabel")} />
|
|
137
200
|
</div>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
) : null}
|
|
149
|
-
</Card>
|
|
201
|
+
}
|
|
202
|
+
/>
|
|
203
|
+
<SettingsRow
|
|
204
|
+
id="workspace-resources"
|
|
205
|
+
label={t("settings.workspaceTitle")}
|
|
206
|
+
description={t("settings.workspaceDescription")}
|
|
207
|
+
control={resourceSettingsButton}
|
|
208
|
+
/>
|
|
209
|
+
{connectAppsRow}
|
|
210
|
+
</SettingsGroup>
|
|
150
211
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
</
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<ChangelogSettingsCard markdown={changelog} />
|
|
191
|
-
</div>
|
|
192
|
-
}
|
|
193
|
-
/>
|
|
194
|
-
</DispatchShell>
|
|
212
|
+
<SettingsGroup id="chat-first">
|
|
213
|
+
<SettingsRow
|
|
214
|
+
label={t("settings.chatFirstTitle")}
|
|
215
|
+
description={t("settings.chatFirstDescription")}
|
|
216
|
+
control={chatFirstSwitch}
|
|
217
|
+
>
|
|
218
|
+
<p className="text-sm leading-6 text-muted-foreground">
|
|
219
|
+
{t("settings.chatFirstSessionWatchDescription")}
|
|
220
|
+
</p>
|
|
221
|
+
{chatFirstStorageAlert}
|
|
222
|
+
</SettingsRow>
|
|
223
|
+
</SettingsGroup>
|
|
224
|
+
</div>
|
|
225
|
+
}
|
|
226
|
+
generalGroups={
|
|
227
|
+
<SettingsGroup id="workspace" title={t("settings.workspaceTitle")}>
|
|
228
|
+
<SettingsRow
|
|
229
|
+
id="chat-first"
|
|
230
|
+
label={t("settings.chatFirstTitle")}
|
|
231
|
+
description={t("settings.chatFirstDescription")}
|
|
232
|
+
control={chatFirstSwitch}
|
|
233
|
+
>
|
|
234
|
+
{chatFirstStorageAlert}
|
|
235
|
+
</SettingsRow>
|
|
236
|
+
<SettingsRow
|
|
237
|
+
id="workspace-resources"
|
|
238
|
+
label={t("settings.resourcesTitle")}
|
|
239
|
+
description={t("settings.workspaceDescription")}
|
|
240
|
+
control={resourceSettingsButton}
|
|
241
|
+
/>
|
|
242
|
+
{connectAppsRow}
|
|
243
|
+
</SettingsGroup>
|
|
244
|
+
}
|
|
245
|
+
whatsNew={
|
|
246
|
+
<div className="mx-auto w-full max-w-2xl">
|
|
247
|
+
<ChangelogSettingsCard markdown={changelog} />
|
|
248
|
+
</div>
|
|
249
|
+
}
|
|
250
|
+
/>
|
|
195
251
|
);
|
|
196
252
|
}
|
|
253
|
+
|
|
254
|
+
export default function SettingsRoute() {
|
|
255
|
+
return <DispatchSettingsPage changelog={packageChangelog} />;
|
|
256
|
+
}
|
|
@@ -972,7 +972,7 @@ function ThreadDetail({ detail }: { detail: ThreadDebugResponse }) {
|
|
|
972
972
|
) : null}
|
|
973
973
|
|
|
974
974
|
<Tabs defaultValue="overview" className="p-5">
|
|
975
|
-
<TabsList
|
|
975
|
+
<TabsList>
|
|
976
976
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
977
977
|
<TabsTrigger value="timeline">Timeline</TabsTrigger>
|
|
978
978
|
<TabsTrigger value="transcript">Transcript</TabsTrigger>
|
|
@@ -755,11 +755,12 @@ function RequestRow({
|
|
|
755
755
|
<div className="flex-1 space-y-1">
|
|
756
756
|
<Label className="text-xs">Key value to provision</Label>
|
|
757
757
|
<Input
|
|
758
|
+
size="sm"
|
|
758
759
|
type="password"
|
|
759
760
|
placeholder="Enter the key value"
|
|
760
761
|
value={secretValue}
|
|
761
762
|
onChange={(e) => setSecretValue(e.target.value)}
|
|
762
|
-
className="
|
|
763
|
+
className="text-sm"
|
|
763
764
|
/>
|
|
764
765
|
</div>
|
|
765
766
|
<Button
|
|
@@ -1028,7 +1029,7 @@ export default function VaultRoute() {
|
|
|
1028
1029
|
initial: { credentialKey: name ?? "" },
|
|
1029
1030
|
})
|
|
1030
1031
|
}
|
|
1031
|
-
|
|
1032
|
+
size="default"
|
|
1032
1033
|
/>
|
|
1033
1034
|
<AddSecretDialog
|
|
1034
1035
|
open={addDialogState.open}
|
|
@@ -19,6 +19,7 @@ vi.mock("@agent-native/core/agent/engine", () => ({
|
|
|
19
19
|
getAgentEngineEntry: vi.fn(() => null),
|
|
20
20
|
isAgentEngineSettingConfigured: vi.fn(() => false),
|
|
21
21
|
isStoredEngineUsable: vi.fn(() => false),
|
|
22
|
+
readDefaultAgentEngineSetting: (...args: any[]) => mocks.getSetting(...args),
|
|
22
23
|
registerBuiltinEngines: () => mocks.registerBuiltinEngines(),
|
|
23
24
|
}));
|
|
24
25
|
|