@sentry/junior-dashboard 0.259.0 → 0.261.0
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 +5 -3
- package/dist/app.js +88 -37
- package/dist/chunks/github-light-QHW3AEMR.js +1 -0
- package/dist/client.js +86 -50
- package/dist/index.js +88 -37
- package/dist/tailwind.css +1 -1
- package/package.json +6 -6
- package/src/client/App.tsx +0 -1
- package/src/client/code.tsx +10 -4
- package/src/client/components/ProfileMenu.tsx +3 -0
- package/src/client/components/README.md +14 -3
- package/src/client/components/Select.tsx +41 -7
- package/src/client/components/ThemeSwitcher.tsx +48 -0
- package/src/client/components/charts/ActivityChart.tsx +6 -7
- package/src/client/components/charts/InputCacheChart.tsx +1 -1
- package/src/client/components/charts/SystemMetricCharts.tsx +3 -3
- package/src/client/components/charts/WorkspaceUsageChart.tsx +1 -1
- package/src/client/components/charts/cache-input.ts +11 -3
- package/src/client/components/controls/DirectorySortSelect.tsx +6 -4
- package/src/client/components/layout/DashboardHeader.tsx +2 -9
- package/src/client/conversations/ConversationBrief.tsx +35 -9
- package/src/client/conversations/ConversationHomeList.tsx +7 -1
- package/src/client/conversations/ConversationMeta.tsx +19 -7
- package/src/client/conversations/ConversationTranscript.tsx +8 -11
- package/src/client/conversations/ConversationWorkspace.tsx +9 -12
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -4
- package/src/client/conversations/EventDetails.tsx +9 -5
- package/src/client/conversations/README.md +6 -0
- package/src/client/conversations/TranscriptContextEventView.tsx +5 -1
- package/src/client/conversations/TranscriptEventView.tsx +48 -9
- package/src/client/conversations/TranscriptMessageView.tsx +6 -1
- package/src/client/conversations/TranscriptStructuredEventView.tsx +4 -2
- package/src/client/conversations/TranscriptText.tsx +28 -20
- package/src/client/conversations/conversationActivityPreview.ts +6 -0
- package/src/client/conversations/eventTranscript.ts +4 -1
- package/src/client/conversations/transcriptMentions.tsx +27 -10
- package/src/client/debouncedValue.ts +14 -0
- package/src/client/pages/automations/AutomationCostChart.tsx +2 -2
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +4 -1
- package/src/client/pages/automations/AutomationExecutionChart.tsx +2 -2
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +3 -3
- package/src/client/pages/automations/AutomationFilters.tsx +4 -3
- package/src/client/pages/code/CodeActivityChart.tsx +4 -4
- package/src/client/pages/code/RepositoryTabs.tsx +3 -2
- package/src/client/pages/dev/ComponentsPage.tsx +11 -3
- package/src/client/pages/dev/ConversationFixture.tsx +29 -0
- package/src/client/pages/dev/SelectFixtures.tsx +62 -0
- package/src/client/pages/locations/LocationActivityChart.tsx +14 -4
- package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +14 -9
- package/src/client/pages/memory/MemoryCostChart.tsx +5 -5
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +9 -2
- package/src/client/pages/memory/MemoryTimeline.tsx +2 -2
- package/src/client/pages/people/ContributionGrid.tsx +8 -8
- package/src/client/pages/people/PeopleActivityChart.tsx +23 -10
- package/src/client/pages/system/BaseSandboxCard.tsx +76 -0
- package/src/client/pages/system/ConversationActivityChart.tsx +2 -2
- package/src/client/pages/system/PluginBarChart.tsx +12 -6
- package/src/client/pages/system/SandboxSizeSummary.tsx +47 -0
- package/src/client/pages/system/WorkspaceDetails.tsx +11 -1
- package/src/client/pages/system/WorkspaceEditor.tsx +19 -0
- package/src/client/pages/system/WorkspaceFormPage.tsx +28 -19
- package/src/client/pages/system/WorkspaceList.tsx +6 -1
- package/src/client/pages/system/WorkspacesPage.tsx +18 -12
- package/src/client/pages/system/workspaceDraft.ts +8 -0
- package/src/client/theme.ts +52 -0
- package/src/client/types.ts +8 -1
- package/src/mock-reporting/fixtures.ts +48 -35
- package/src/shell.ts +4 -1
- package/src/tailwind.css +109 -0
- package/src/theme.ts +49 -0
- package/src/client/pages/system/BaselineSnapshotCard.tsx +0 -18
|
@@ -2,9 +2,9 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
3
|
import { useNavigate, useParams } from "react-router";
|
|
4
4
|
import {
|
|
5
|
+
workspaceListSchema,
|
|
5
6
|
workspaceSchema,
|
|
6
|
-
type
|
|
7
|
-
type WorkspaceReport,
|
|
7
|
+
type WorkspaceListReport,
|
|
8
8
|
} from "@sentry/junior/api/schema";
|
|
9
9
|
|
|
10
10
|
import { EmptyTelemetry } from "../../components/EmptyTelemetry";
|
|
@@ -16,10 +16,7 @@ import { fetchDashboardJson, post, put } from "../../http";
|
|
|
16
16
|
import { SystemPageLayout } from "./SystemPageLayout";
|
|
17
17
|
import { WorkspaceDetails } from "./WorkspaceDetails";
|
|
18
18
|
import { WorkspaceEditor } from "./WorkspaceEditor";
|
|
19
|
-
import {
|
|
20
|
-
readWorkspaceApiError,
|
|
21
|
-
workspacesQueryKey,
|
|
22
|
-
} from "./WorkspacesPage";
|
|
19
|
+
import { readWorkspaceApiError, workspacesQueryKey } from "./WorkspacesPage";
|
|
23
20
|
import {
|
|
24
21
|
canSaveWorkspaceDraft,
|
|
25
22
|
createWorkspaceDraft,
|
|
@@ -56,6 +53,14 @@ export function WorkspaceFormPage() {
|
|
|
56
53
|
),
|
|
57
54
|
retry: false,
|
|
58
55
|
});
|
|
56
|
+
// Shares the list cache so the default size is usually loaded already.
|
|
57
|
+
const workspacesQuery = useQuery({
|
|
58
|
+
queryKey: workspacesQueryKey,
|
|
59
|
+
queryFn: ({ signal }) =>
|
|
60
|
+
fetchDashboardJson(workspaceListSchema, "/api/workspaces", signal),
|
|
61
|
+
retry: false,
|
|
62
|
+
});
|
|
63
|
+
const defaultVcpus = workspacesQuery.data?.defaultVcpus ?? null;
|
|
59
64
|
|
|
60
65
|
// Seed the edit draft once per Workspace id. editWorkspaceDraft assigns fresh
|
|
61
66
|
// repo keys, so calling it during every query-driven render remounts fields.
|
|
@@ -87,18 +92,17 @@ export function WorkspaceFormPage() {
|
|
|
87
92
|
},
|
|
88
93
|
onSuccess: async (workspace) => {
|
|
89
94
|
await queryClient.cancelQueries({ queryKey: workspacesQueryKey });
|
|
90
|
-
queryClient.setQueryData<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}));
|
|
95
|
+
queryClient.setQueryData<WorkspaceListReport>(
|
|
96
|
+
workspacesQueryKey,
|
|
97
|
+
(current) =>
|
|
98
|
+
current && {
|
|
99
|
+
...current,
|
|
100
|
+
workspaces: [
|
|
101
|
+
workspace,
|
|
102
|
+
...current.workspaces.filter((item) => item.id !== workspace.id),
|
|
103
|
+
].sort((left, right) => left.name.localeCompare(right.name)),
|
|
104
|
+
},
|
|
105
|
+
);
|
|
102
106
|
navigate("/system/workspaces");
|
|
103
107
|
},
|
|
104
108
|
});
|
|
@@ -135,7 +139,11 @@ export function WorkspaceFormPage() {
|
|
|
135
139
|
/>
|
|
136
140
|
|
|
137
141
|
{editing && workspaceQuery.data ? (
|
|
138
|
-
<WorkspaceDetails
|
|
142
|
+
<WorkspaceDetails
|
|
143
|
+
defaultVcpus={defaultVcpus}
|
|
144
|
+
range={range}
|
|
145
|
+
workspace={workspaceQuery.data}
|
|
146
|
+
/>
|
|
139
147
|
) : null}
|
|
140
148
|
|
|
141
149
|
{workspaceQuery.error ? (
|
|
@@ -150,6 +158,7 @@ export function WorkspaceFormPage() {
|
|
|
150
158
|
) : draft ? (
|
|
151
159
|
<WorkspaceEditor
|
|
152
160
|
busy={saveMutation.isPending}
|
|
161
|
+
defaultVcpus={defaultVcpus}
|
|
153
162
|
canSave={canSaveWorkspaceDraft(draft, saveMutation.isPending)}
|
|
154
163
|
draft={draft}
|
|
155
164
|
editing={editing}
|
|
@@ -6,9 +6,11 @@ import { Button } from "../../components/Button";
|
|
|
6
6
|
import { EmptyTelemetry } from "../../components/EmptyTelemetry";
|
|
7
7
|
import { SelectableRow } from "../../components/SelectableRow";
|
|
8
8
|
import { Card } from "../../components/layout/Card";
|
|
9
|
+
import { formatSandboxSize } from "./SandboxSizeSummary";
|
|
9
10
|
|
|
10
11
|
type WorkspaceListProps = {
|
|
11
12
|
busy: boolean;
|
|
13
|
+
defaultVcpus: number | null;
|
|
12
14
|
onDelete(workspace: WorkspaceReport): void;
|
|
13
15
|
workspaces: WorkspaceReport[];
|
|
14
16
|
};
|
|
@@ -29,6 +31,7 @@ export function WorkspaceList(props: WorkspaceListProps) {
|
|
|
29
31
|
{props.workspaces.map((workspace) => (
|
|
30
32
|
<WorkspaceListItem
|
|
31
33
|
busy={props.busy}
|
|
34
|
+
defaultVcpus={props.defaultVcpus}
|
|
32
35
|
key={workspace.id}
|
|
33
36
|
onDelete={props.onDelete}
|
|
34
37
|
workspace={workspace}
|
|
@@ -42,6 +45,7 @@ export function WorkspaceList(props: WorkspaceListProps) {
|
|
|
42
45
|
|
|
43
46
|
function WorkspaceListItem(props: {
|
|
44
47
|
busy: boolean;
|
|
48
|
+
defaultVcpus: number | null;
|
|
45
49
|
onDelete(workspace: WorkspaceReport): void;
|
|
46
50
|
workspace: WorkspaceReport;
|
|
47
51
|
}) {
|
|
@@ -72,7 +76,8 @@ function WorkspaceListItem(props: {
|
|
|
72
76
|
</span>
|
|
73
77
|
<span className="font-mono text-xs text-dashboard-text-muted">
|
|
74
78
|
{workspace.repos.length} repo
|
|
75
|
-
{workspace.repos.length === 1 ? "" : "s"}
|
|
79
|
+
{workspace.repos.length === 1 ? "" : "s"} ·{" "}
|
|
80
|
+
{formatSandboxSize(workspace.vcpus ?? props.defaultVcpus)}
|
|
76
81
|
</span>
|
|
77
82
|
</span>
|
|
78
83
|
<span className="mt-1.5 block truncate font-mono text-xs text-dashboard-text-muted">
|
|
@@ -2,7 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
2
2
|
import { Plus } from "lucide-react";
|
|
3
3
|
import {
|
|
4
4
|
workspaceListSchema,
|
|
5
|
-
type
|
|
5
|
+
type WorkspaceListReport,
|
|
6
6
|
type WorkspaceReport,
|
|
7
7
|
} from "@sentry/junior/api/schema";
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
deleteDashboardResource,
|
|
18
18
|
fetchDashboardJson,
|
|
19
19
|
} from "../../http";
|
|
20
|
-
import {
|
|
20
|
+
import { BaseSandboxCard } from "./BaseSandboxCard";
|
|
21
21
|
import { SystemPageLayout, SystemRouteLoading } from "./SystemPageLayout";
|
|
22
22
|
import { WorkspaceList } from "./WorkspaceList";
|
|
23
23
|
|
|
@@ -55,15 +55,16 @@ export function WorkspacesPage() {
|
|
|
55
55
|
},
|
|
56
56
|
onSuccess: async (workspace) => {
|
|
57
57
|
await queryClient.cancelQueries({ queryKey: workspacesQueryKey });
|
|
58
|
-
queryClient.setQueryData<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
queryClient.setQueryData<WorkspaceListReport>(
|
|
59
|
+
workspacesQueryKey,
|
|
60
|
+
(current) =>
|
|
61
|
+
current && {
|
|
62
|
+
...current,
|
|
63
|
+
workspaces: current.workspaces.filter(
|
|
64
|
+
(item) => item.id !== workspace.id,
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
);
|
|
67
68
|
},
|
|
68
69
|
});
|
|
69
70
|
|
|
@@ -80,6 +81,7 @@ export function WorkspacesPage() {
|
|
|
80
81
|
|
|
81
82
|
const workspaces = workspacesQuery.data?.workspaces ?? [];
|
|
82
83
|
const baselineSnapshot = workspacesQuery.data?.baselineSnapshot ?? null;
|
|
84
|
+
const defaultVcpus = workspacesQuery.data?.defaultVcpus ?? null;
|
|
83
85
|
return (
|
|
84
86
|
<SystemPageLayout>
|
|
85
87
|
<PageHeader
|
|
@@ -111,11 +113,15 @@ export function WorkspacesPage() {
|
|
|
111
113
|
) : null}
|
|
112
114
|
|
|
113
115
|
{!workspacesQuery.error ? (
|
|
114
|
-
<
|
|
116
|
+
<BaseSandboxCard
|
|
117
|
+
defaultVcpus={defaultVcpus}
|
|
118
|
+
snapshot={baselineSnapshot}
|
|
119
|
+
/>
|
|
115
120
|
) : null}
|
|
116
121
|
|
|
117
122
|
<WorkspaceList
|
|
118
123
|
busy={deleteMutation.isPending}
|
|
124
|
+
defaultVcpus={defaultVcpus}
|
|
119
125
|
onDelete={(workspace) => {
|
|
120
126
|
if (
|
|
121
127
|
window.confirm(
|
|
@@ -9,6 +9,8 @@ export type RepoDraft = {
|
|
|
9
9
|
export type WorkspaceDraft = {
|
|
10
10
|
name: string;
|
|
11
11
|
setupScript: string;
|
|
12
|
+
/** Text input value. Empty means the app default. */
|
|
13
|
+
vcpus: string;
|
|
12
14
|
repos: RepoDraft[];
|
|
13
15
|
};
|
|
14
16
|
|
|
@@ -24,6 +26,7 @@ export function createWorkspaceDraft(): WorkspaceDraft {
|
|
|
24
26
|
return {
|
|
25
27
|
name: "",
|
|
26
28
|
setupScript: "",
|
|
29
|
+
vcpus: "",
|
|
27
30
|
repos: [createRepoDraft()],
|
|
28
31
|
};
|
|
29
32
|
}
|
|
@@ -32,6 +35,7 @@ export function editWorkspaceDraft(workspace: WorkspaceReport): WorkspaceDraft {
|
|
|
32
35
|
return {
|
|
33
36
|
name: workspace.name,
|
|
34
37
|
setupScript: workspace.setupScript,
|
|
38
|
+
vcpus: workspace.vcpus === null ? "" : String(workspace.vcpus),
|
|
35
39
|
repos:
|
|
36
40
|
workspace.repos.length > 0
|
|
37
41
|
? workspace.repos.map((repo) => ({
|
|
@@ -48,6 +52,7 @@ export function workspaceDraftBody(draft: WorkspaceDraft) {
|
|
|
48
52
|
return {
|
|
49
53
|
name: draft.name.trim(),
|
|
50
54
|
setupScript: draft.setupScript,
|
|
55
|
+
vcpus: draft.vcpus.trim() ? Number(draft.vcpus.trim()) : null,
|
|
51
56
|
repos: draft.repos.map((repo) => ({
|
|
52
57
|
provider: repo.provider.trim(),
|
|
53
58
|
repo: repo.repo.trim(),
|
|
@@ -60,6 +65,9 @@ export function canSaveWorkspaceDraft(
|
|
|
60
65
|
busy: boolean,
|
|
61
66
|
): boolean {
|
|
62
67
|
if (busy || !draft.name.trim()) return false;
|
|
68
|
+
if (draft.vcpus.trim() && !/^[1-9]\d*$/.test(draft.vcpus.trim())) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
63
71
|
if (draft.repos.some((repo) => !repo.provider.trim() || !repo.repo.trim())) {
|
|
64
72
|
return false;
|
|
65
73
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DASHBOARD_THEME_CHANGE_EVENT,
|
|
5
|
+
DASHBOARD_THEME_STORAGE_KEY,
|
|
6
|
+
type DashboardThemeChoice,
|
|
7
|
+
} from "../theme";
|
|
8
|
+
|
|
9
|
+
function readThemeChoice(): DashboardThemeChoice {
|
|
10
|
+
try {
|
|
11
|
+
const choice = window.localStorage.getItem(DASHBOARD_THEME_STORAGE_KEY);
|
|
12
|
+
return choice === "light" || choice === "dark" ? choice : "system";
|
|
13
|
+
} catch {
|
|
14
|
+
return "system";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function subscribe(onChange: () => void): () => void {
|
|
19
|
+
window.addEventListener(DASHBOARD_THEME_CHANGE_EVENT, onChange);
|
|
20
|
+
window.addEventListener("storage", onChange);
|
|
21
|
+
return () => {
|
|
22
|
+
window.removeEventListener(DASHBOARD_THEME_CHANGE_EVENT, onChange);
|
|
23
|
+
window.removeEventListener("storage", onChange);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Save a theme choice for this browser. The shell theme script applies it
|
|
29
|
+
* and keeps following the system color scheme for `system`.
|
|
30
|
+
*/
|
|
31
|
+
function saveThemeChoice(choice: DashboardThemeChoice): void {
|
|
32
|
+
try {
|
|
33
|
+
if (choice === "system") {
|
|
34
|
+
window.localStorage.removeItem(DASHBOARD_THEME_STORAGE_KEY);
|
|
35
|
+
} else {
|
|
36
|
+
window.localStorage.setItem(DASHBOARD_THEME_STORAGE_KEY, choice);
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Storage is blocked, so the dashboard keeps following the system.
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
window.dispatchEvent(new Event(DASHBOARD_THEME_CHANGE_EVENT));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Read and update this browser's dashboard theme choice. */
|
|
46
|
+
export function useDashboardThemeChoice(): [
|
|
47
|
+
DashboardThemeChoice,
|
|
48
|
+
(choice: DashboardThemeChoice) => void,
|
|
49
|
+
] {
|
|
50
|
+
const choice = useSyncExternalStore(subscribe, readThemeChoice);
|
|
51
|
+
return [choice, saveThemeChoice];
|
|
52
|
+
}
|
package/src/client/types.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from "@sentry/junior/api/schema";
|
|
10
10
|
import type {
|
|
11
11
|
ActorIdentity,
|
|
12
|
+
ConversationReportMessageEvent,
|
|
12
13
|
ConversationStatsReport,
|
|
13
14
|
ConversationSummaryReport,
|
|
14
15
|
ConversationTurnFailureCode,
|
|
@@ -51,6 +52,8 @@ export type TranscriptViewSubagentPart = {
|
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
export type TranscriptViewContextEventPart = {
|
|
55
|
+
/** People mentioned in the summary, resolved by the API. */
|
|
56
|
+
mentions?: ActorIdentity[];
|
|
54
57
|
event:
|
|
55
58
|
| {
|
|
56
59
|
createdAt: string;
|
|
@@ -81,6 +84,8 @@ export type TranscriptViewContextEventPart = {
|
|
|
81
84
|
};
|
|
82
85
|
|
|
83
86
|
export type TranscriptViewStructuredEventPart = {
|
|
87
|
+
/** People mentioned in the preview and details, resolved by the API. */
|
|
88
|
+
mentions?: ActorIdentity[];
|
|
84
89
|
name: string;
|
|
85
90
|
namespace: string;
|
|
86
91
|
presentation: ConversationEventPresentation;
|
|
@@ -120,9 +125,11 @@ export type TranscriptViewMessage = {
|
|
|
120
125
|
eventObjectType?: ObjectType;
|
|
121
126
|
/** Short summary supplied by the Event publisher. */
|
|
122
127
|
trustedSummary?: string;
|
|
128
|
+
/** Every Event when several arrived together in one message. */
|
|
129
|
+
events?: ConversationReportMessageEvent[];
|
|
123
130
|
/** Whether the source message addressed Junior directly. */
|
|
124
131
|
explicitMention?: boolean;
|
|
125
|
-
/** People mentioned in the
|
|
132
|
+
/** People mentioned in the event text, resolved by the API. */
|
|
126
133
|
mentions?: ActorIdentity[];
|
|
127
134
|
/** Whether a non-mention message was used as input to a turn. */
|
|
128
135
|
context?: boolean;
|
|
@@ -168,7 +168,7 @@ function reportEvent(
|
|
|
168
168
|
seq: number,
|
|
169
169
|
createdAt: string,
|
|
170
170
|
data: ConversationReportEventData,
|
|
171
|
-
metadata?: Pick<ConversationReportEvent, "model" | "modelCall">,
|
|
171
|
+
metadata?: Pick<ConversationReportEvent, "mentions" | "model" | "modelCall">,
|
|
172
172
|
): ConversationReportEvent {
|
|
173
173
|
return { seq, createdAt, data, ...metadata };
|
|
174
174
|
}
|
|
@@ -1428,48 +1428,58 @@ function eventAutomationRunConversation(
|
|
|
1428
1428
|
credentialUser: "Alex Rivera",
|
|
1429
1429
|
},
|
|
1430
1430
|
events: [
|
|
1431
|
-
reportEvent(
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1431
|
+
reportEvent(
|
|
1432
|
+
0,
|
|
1433
|
+
startedAt,
|
|
1434
|
+
{
|
|
1435
|
+
type: "message",
|
|
1436
|
+
messageId: EVENT_AUTOMATION_RUN_CONVERSATION_ID,
|
|
1437
|
+
role: "user",
|
|
1438
|
+
// Dispatch input is authored by the dispatch owner, not a person.
|
|
1439
|
+
actorIdentity: {
|
|
1440
|
+
fullName: "junior",
|
|
1441
|
+
slackUserId: "junior",
|
|
1442
|
+
slackUserName: "junior",
|
|
1443
|
+
},
|
|
1444
|
+
text: [
|
|
1445
|
+
"[task]",
|
|
1446
|
+
"",
|
|
1447
|
+
"This is a task, not a message from a person.",
|
|
1448
|
+
"",
|
|
1449
|
+
"About: Issue · ACME-42",
|
|
1450
|
+
'Created by: <@U123>. "Me" and "my" in the instructions mean this person: mention them in the message. If the instructions do not say "me" or "my", do not mention this person.',
|
|
1451
|
+
"Instructions: Summarize the closed issue",
|
|
1452
|
+
"",
|
|
1453
|
+
"Trusted summary: ACME-42 Checkout fails for saved cards was closed by Morgan Lee.",
|
|
1454
|
+
"",
|
|
1455
|
+
"End with `finishAutomationRun`.",
|
|
1456
|
+
].join("\n"),
|
|
1440
1457
|
},
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
"",
|
|
1444
|
-
"This is a task, not a message from a person.",
|
|
1445
|
-
"",
|
|
1446
|
-
"About: Issue · ACME-42",
|
|
1447
|
-
'Created by: <@U123>. "Me" and "my" in the instructions mean this person: mention them in the message. If the instructions do not say "me" or "my", do not mention this person.',
|
|
1448
|
-
"Instructions: Summarize the closed issue",
|
|
1449
|
-
"",
|
|
1450
|
-
"Trusted summary: ACME-42 Checkout fails for saved cards was closed by Morgan Lee.",
|
|
1451
|
-
"",
|
|
1452
|
-
"End with `finishAutomationRun`.",
|
|
1453
|
-
].join("\n"),
|
|
1454
|
-
mentions: [MOCK_MENTION_MORGAN],
|
|
1455
|
-
}),
|
|
1458
|
+
{ mentions: [MOCK_MENTION_MORGAN] },
|
|
1459
|
+
),
|
|
1456
1460
|
reportEvent(1, at(1_000), {
|
|
1457
1461
|
type: "turn_lifecycle",
|
|
1458
1462
|
turnId: "dispatch:event-1",
|
|
1459
1463
|
state: "started",
|
|
1460
1464
|
inputMessageIds: [EVENT_AUTOMATION_RUN_CONVERSATION_ID],
|
|
1461
1465
|
}),
|
|
1462
|
-
reportEvent(
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1466
|
+
reportEvent(
|
|
1467
|
+
2,
|
|
1468
|
+
at(13_000),
|
|
1469
|
+
{
|
|
1470
|
+
type: "message",
|
|
1471
|
+
messageId: `${EVENT_AUTOMATION_RUN_CONVERSATION_ID}:reply`,
|
|
1472
|
+
role: "assistant",
|
|
1473
|
+
text: "<@U123> ACME-42 is closed. The saved-card checkout failure was fixed by the token refresh change and verified in production. cc <@U456> and <@U999> in <#C42|proj-checkout>.",
|
|
1474
|
+
},
|
|
1467
1475
|
// U456 has no People profile, and U999 is not resolved.
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1476
|
+
{
|
|
1477
|
+
mentions: [
|
|
1478
|
+
MOCK_MENTION_MORGAN,
|
|
1479
|
+
{ fullName: "Casey Nguyen", slackUserId: "U456" },
|
|
1480
|
+
],
|
|
1481
|
+
},
|
|
1482
|
+
),
|
|
1473
1483
|
reportEvent(3, at(14_000), {
|
|
1474
1484
|
type: "turn_lifecycle",
|
|
1475
1485
|
turnId: "dispatch:event-1",
|
|
@@ -1601,6 +1611,9 @@ function summaryFromConversation(
|
|
|
1601
1611
|
activityEvent?.data.type === "message" && activityEvent.data.text
|
|
1602
1612
|
? {
|
|
1603
1613
|
createdAt: activityEvent.createdAt,
|
|
1614
|
+
...(activityEvent.mentions
|
|
1615
|
+
? { mentions: activityEvent.mentions }
|
|
1616
|
+
: undefined),
|
|
1604
1617
|
role: activityEvent.data.role as "assistant" | "user",
|
|
1605
1618
|
text: activityEvent.data.text,
|
|
1606
1619
|
}
|
package/src/shell.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
dashboardTailwindAsset,
|
|
10
10
|
} from "./assets";
|
|
11
11
|
import { dashboardRainbowProgressClass } from "./dashboardLoader";
|
|
12
|
+
import { DASHBOARD_THEME_COLORS, DASHBOARD_THEME_SCRIPT } from "./theme";
|
|
12
13
|
|
|
13
14
|
const DASHBOARD_CLIENT_VERSION = Date.now().toString(36);
|
|
14
15
|
export const DASHBOARD_CLIENT_PATH = "/_junior/dashboard/client.js";
|
|
@@ -16,7 +17,7 @@ export const DASHBOARD_AVATAR_HEADER_PATH = "/_junior/dashboard/avatar.png";
|
|
|
16
17
|
export const DASHBOARD_INSTALL_ICON_PATH = "/_junior/dashboard/icon-512.png";
|
|
17
18
|
export const DASHBOARD_MANIFEST_PATH =
|
|
18
19
|
"/_junior/dashboard/manifest.webmanifest";
|
|
19
|
-
const DASHBOARD_THEME_COLOR =
|
|
20
|
+
const DASHBOARD_THEME_COLOR = DASHBOARD_THEME_COLORS.dark;
|
|
20
21
|
const DASHBOARD_BACKGROUND_COLOR = "#000000";
|
|
21
22
|
|
|
22
23
|
function escapeHtml(value: string): string {
|
|
@@ -224,6 +225,7 @@ export function renderDashboard(basePath: string, agentName: string): Response {
|
|
|
224
225
|
<meta charset="utf-8" />
|
|
225
226
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover, interactive-widget=resizes-content" />
|
|
226
227
|
<meta name="theme-color" content="${DASHBOARD_THEME_COLOR}" />
|
|
228
|
+
<script>${DASHBOARD_THEME_SCRIPT}</script>
|
|
227
229
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
228
230
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
229
231
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
@@ -334,6 +336,7 @@ export function renderForbiddenPage(agentName: string): Response {
|
|
|
334
336
|
<meta charset="utf-8" />
|
|
335
337
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover, interactive-widget=resizes-content" />
|
|
336
338
|
<title>${escapeHtml(agentName)} access denied</title>
|
|
339
|
+
<script>${DASHBOARD_THEME_SCRIPT}</script>
|
|
337
340
|
<style>
|
|
338
341
|
${readDashboardTailwind()}
|
|
339
342
|
</style>
|
package/src/tailwind.css
CHANGED
|
@@ -108,6 +108,115 @@
|
|
|
108
108
|
--color-dashboard-scrollbar-thumb-hover: rgb(255 255 255 / 0.2);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/*
|
|
112
|
+
* Light theme. src/theme.ts sets data-theme on <html> before first paint from
|
|
113
|
+
* the saved choice or the system color scheme. Each dashboard token above gets
|
|
114
|
+
* a light value here.
|
|
115
|
+
*/
|
|
116
|
+
:root[data-theme="light"] {
|
|
117
|
+
--dashboard-color-scheme: light;
|
|
118
|
+
|
|
119
|
+
--color-dashboard-bg: #ffffff;
|
|
120
|
+
--color-dashboard-bg-elevated: #ffffff;
|
|
121
|
+
--color-dashboard-surface-panel: #f9f9fa;
|
|
122
|
+
--color-dashboard-surface-active: #e9e9ec;
|
|
123
|
+
--color-dashboard-surface-raised: #ffffff;
|
|
124
|
+
--color-dashboard-surface-hover: #f1f1f3;
|
|
125
|
+
--color-dashboard-control: #ffffff;
|
|
126
|
+
--color-dashboard-ink: #ffffff;
|
|
127
|
+
|
|
128
|
+
--color-dashboard-text: #18181b;
|
|
129
|
+
--color-dashboard-text-muted: #52525b;
|
|
130
|
+
--color-dashboard-text-subtle: #3f3f46;
|
|
131
|
+
--color-dashboard-text-faint: #71717a;
|
|
132
|
+
--color-dashboard-text-solid: #09090b;
|
|
133
|
+
--color-dashboard-text-inverse: #ffffff;
|
|
134
|
+
|
|
135
|
+
--color-dashboard-accent: #7553ff;
|
|
136
|
+
--color-dashboard-accent-text: #5b3bd9;
|
|
137
|
+
--color-dashboard-focus: #7553ff;
|
|
138
|
+
|
|
139
|
+
--color-dashboard-border-subtle: rgb(0 0 0 / 0.06);
|
|
140
|
+
--color-dashboard-border: rgb(0 0 0 / 0.09);
|
|
141
|
+
--color-dashboard-border-strong: rgb(0 0 0 / 0.11);
|
|
142
|
+
--color-dashboard-border-emphasis: rgb(0 0 0 / 0.16);
|
|
143
|
+
--color-dashboard-border-interactive: rgb(0 0 0 / 0.26);
|
|
144
|
+
--color-dashboard-border-heavy: rgb(0 0 0 / 0.32);
|
|
145
|
+
|
|
146
|
+
--color-dashboard-fill-faint: rgb(0 0 0 / 0.02);
|
|
147
|
+
--color-dashboard-fill-soft: rgb(0 0 0 / 0.035);
|
|
148
|
+
--color-dashboard-fill-hover: rgb(0 0 0 / 0.05);
|
|
149
|
+
--color-dashboard-fill-strong: rgb(0 0 0 / 0.08);
|
|
150
|
+
|
|
151
|
+
--color-dashboard-overlay-soft: rgb(0 0 0 / 0.06);
|
|
152
|
+
--color-dashboard-overlay: rgb(0 0 0 / 0.35);
|
|
153
|
+
|
|
154
|
+
--color-dashboard-scrollbar-track: rgb(0 0 0 / 0.02);
|
|
155
|
+
--color-dashboard-scrollbar-thumb: rgb(0 0 0 / 0.14);
|
|
156
|
+
--color-dashboard-scrollbar-thumb-hover: rgb(0 0 0 / 0.26);
|
|
157
|
+
|
|
158
|
+
/*
|
|
159
|
+
* Status and chart hues are tuned for the dark canvas: the UI uses their
|
|
160
|
+
* light shades (50-400). Mirror those shades (50 to 950, 100 to 900, and so
|
|
161
|
+
* on) so the same classes keep their contrast on the light canvas. Shades
|
|
162
|
+
* 500 and darker keep their values. Add a hue here when the UI starts to
|
|
163
|
+
* use it.
|
|
164
|
+
*/
|
|
165
|
+
--color-amber-50: oklch(27.9% 0.077 45.635);
|
|
166
|
+
--color-amber-100: oklch(41.4% 0.112 45.904);
|
|
167
|
+
--color-amber-200: oklch(47.3% 0.137 46.201);
|
|
168
|
+
--color-amber-300: oklch(55.5% 0.163 48.998);
|
|
169
|
+
--color-amber-400: oklch(66.6% 0.179 58.318);
|
|
170
|
+
--color-cyan-50: oklch(30.2% 0.056 229.695);
|
|
171
|
+
--color-cyan-100: oklch(39.8% 0.07 227.392);
|
|
172
|
+
--color-cyan-200: oklch(45% 0.085 224.283);
|
|
173
|
+
--color-cyan-300: oklch(52% 0.105 223.128);
|
|
174
|
+
--color-cyan-400: oklch(60.9% 0.126 221.723);
|
|
175
|
+
--color-emerald-50: oklch(26.2% 0.051 172.552);
|
|
176
|
+
--color-emerald-100: oklch(37.8% 0.077 168.94);
|
|
177
|
+
--color-emerald-200: oklch(43.2% 0.095 166.913);
|
|
178
|
+
--color-emerald-300: oklch(50.8% 0.118 165.612);
|
|
179
|
+
--color-emerald-400: oklch(59.6% 0.145 163.225);
|
|
180
|
+
--color-fuchsia-50: oklch(29.3% 0.136 325.661);
|
|
181
|
+
--color-fuchsia-100: oklch(40.1% 0.17 325.612);
|
|
182
|
+
--color-fuchsia-200: oklch(45.2% 0.211 324.591);
|
|
183
|
+
--color-fuchsia-300: oklch(51.8% 0.253 323.949);
|
|
184
|
+
--color-fuchsia-400: oklch(59.1% 0.293 322.896);
|
|
185
|
+
--color-orange-50: oklch(26.6% 0.079 36.259);
|
|
186
|
+
--color-orange-100: oklch(40.8% 0.123 38.172);
|
|
187
|
+
--color-orange-200: oklch(47% 0.157 37.304);
|
|
188
|
+
--color-orange-300: oklch(55.3% 0.195 38.402);
|
|
189
|
+
--color-orange-400: oklch(64.6% 0.222 41.116);
|
|
190
|
+
--color-red-50: oklch(25.8% 0.092 26.042);
|
|
191
|
+
--color-red-100: oklch(39.6% 0.141 25.723);
|
|
192
|
+
--color-red-200: oklch(44.4% 0.177 26.899);
|
|
193
|
+
--color-red-300: oklch(50.5% 0.213 27.518);
|
|
194
|
+
--color-red-400: oklch(57.7% 0.245 27.325);
|
|
195
|
+
--color-rose-50: oklch(27.1% 0.105 12.094);
|
|
196
|
+
--color-rose-100: oklch(41% 0.159 10.272);
|
|
197
|
+
--color-rose-200: oklch(45.5% 0.188 13.697);
|
|
198
|
+
--color-rose-300: oklch(51.4% 0.222 16.935);
|
|
199
|
+
--color-rose-400: oklch(58.6% 0.253 17.585);
|
|
200
|
+
--color-sky-50: oklch(29.3% 0.066 243.157);
|
|
201
|
+
--color-sky-100: oklch(39.1% 0.09 240.876);
|
|
202
|
+
--color-sky-200: oklch(44.3% 0.11 240.79);
|
|
203
|
+
--color-sky-300: oklch(50% 0.134 242.749);
|
|
204
|
+
--color-sky-400: oklch(58.8% 0.158 241.966);
|
|
205
|
+
--color-slate-50: oklch(12.9% 0.042 264.695);
|
|
206
|
+
--color-slate-100: oklch(20.8% 0.042 265.755);
|
|
207
|
+
--color-slate-200: oklch(27.9% 0.041 260.031);
|
|
208
|
+
--color-slate-300: oklch(37.2% 0.044 257.287);
|
|
209
|
+
--color-slate-400: oklch(44.6% 0.043 257.281);
|
|
210
|
+
--color-violet-50: oklch(28.3% 0.141 291.089);
|
|
211
|
+
--color-violet-100: oklch(38% 0.189 293.745);
|
|
212
|
+
--color-violet-200: oklch(43.2% 0.232 292.759);
|
|
213
|
+
--color-violet-300: oklch(49.1% 0.27 292.581);
|
|
214
|
+
--color-violet-400: oklch(54.1% 0.281 293.009);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Style one-off values that the token and palette mirror cannot cover. */
|
|
218
|
+
@custom-variant light (&:where([data-theme="light"], [data-theme="light"] *));
|
|
219
|
+
|
|
111
220
|
@layer base {
|
|
112
221
|
:root {
|
|
113
222
|
color-scheme: var(--dashboard-color-scheme, dark);
|
package/src/theme.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Theme the dashboard shows: the saved choice, or the system color scheme. */
|
|
2
|
+
export type DashboardThemeChoice = "dark" | "light" | "system";
|
|
3
|
+
|
|
4
|
+
/** Browser storage key for the theme choice. No value means `system`. */
|
|
5
|
+
export const DASHBOARD_THEME_STORAGE_KEY = "junior-dashboard-theme";
|
|
6
|
+
|
|
7
|
+
/** Window event that tells the theme script to apply a new saved choice. */
|
|
8
|
+
export const DASHBOARD_THEME_CHANGE_EVENT = "junior:dashboard-theme";
|
|
9
|
+
|
|
10
|
+
/** Browser chrome color for each theme. Dark is the default. */
|
|
11
|
+
export const DASHBOARD_THEME_COLORS = {
|
|
12
|
+
dark: "#000000",
|
|
13
|
+
light: "#ffffff",
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Inline head script that sets `data-theme` on `<html>` before first paint.
|
|
18
|
+
* It also follows system color scheme changes, choices from other tabs, and
|
|
19
|
+
* the change event, so it owns every theme update after page load.
|
|
20
|
+
*/
|
|
21
|
+
export const DASHBOARD_THEME_SCRIPT = `(function () {
|
|
22
|
+
var media = window.matchMedia("(prefers-color-scheme: light)");
|
|
23
|
+
function apply() {
|
|
24
|
+
var choice = null;
|
|
25
|
+
try {
|
|
26
|
+
choice = window.localStorage.getItem(${JSON.stringify(DASHBOARD_THEME_STORAGE_KEY)});
|
|
27
|
+
} catch (_error) {}
|
|
28
|
+
var theme =
|
|
29
|
+
choice === "light" || choice === "dark"
|
|
30
|
+
? choice
|
|
31
|
+
: media.matches
|
|
32
|
+
? "light"
|
|
33
|
+
: "dark";
|
|
34
|
+
document.documentElement.dataset.theme = theme;
|
|
35
|
+
var meta = document.querySelector('meta[name="theme-color"]');
|
|
36
|
+
if (meta) {
|
|
37
|
+
meta.setAttribute(
|
|
38
|
+
"content",
|
|
39
|
+
theme === "light" ? ${JSON.stringify(DASHBOARD_THEME_COLORS.light)} : ${JSON.stringify(DASHBOARD_THEME_COLORS.dark)},
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
apply();
|
|
44
|
+
media.addEventListener("change", apply);
|
|
45
|
+
window.addEventListener("storage", function (event) {
|
|
46
|
+
if (event.key === null || event.key === ${JSON.stringify(DASHBOARD_THEME_STORAGE_KEY)}) apply();
|
|
47
|
+
});
|
|
48
|
+
window.addEventListener(${JSON.stringify(DASHBOARD_THEME_CHANGE_EVENT)}, apply);
|
|
49
|
+
})();`;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { BaselineSnapshotReport } from "@sentry/junior/api/schema";
|
|
2
|
-
|
|
3
|
-
import { SnapshotSummary } from "./SnapshotSummary";
|
|
4
|
-
|
|
5
|
-
/** Show the install-wide baseline Sandbox snapshot used without a Workspace. */
|
|
6
|
-
export function BaselineSnapshotCard(props: {
|
|
7
|
-
snapshot: BaselineSnapshotReport | null;
|
|
8
|
-
}) {
|
|
9
|
-
return (
|
|
10
|
-
<SnapshotSummary
|
|
11
|
-
description="Default Sandboxes start from this install-wide snapshot when no Workspace is selected."
|
|
12
|
-
emptyDescription="No baseline snapshot is registered yet. The first base Sandbox prepare creates one."
|
|
13
|
-
headingId="baseline-snapshot-heading"
|
|
14
|
-
snapshot={props.snapshot}
|
|
15
|
-
title="Baseline snapshot"
|
|
16
|
-
/>
|
|
17
|
-
);
|
|
18
|
-
}
|