@steerable/agent-shell-web 0.6.53 → 0.6.56
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 +3 -4
- package/package.json +4 -4
- package/src/AppShell.tsx +3 -3
- package/src/brand.ts +1 -1
- package/src/components/AgentSidebar.tsx +64 -56
- package/src/components/ChatHeader.tsx +41 -25
- package/src/components/ConfirmDialog.tsx +4 -3
- package/src/components/CreateProjectModal.tsx +31 -21
- package/src/components/LocalLlmSettingsModal.tsx +4 -3
- package/src/components/SidebarRelease.tsx +6 -5
- package/src/components/TerminalPanel.tsx +6 -5
- package/src/components/TerminalView.tsx +15 -10
- package/src/components/chat/ApprovalModal.tsx +40 -26
- package/src/components/chat/ApprovalPromptProvider.tsx +5 -5
- package/src/components/chat/AskUserPromptProvider.tsx +4 -4
- package/src/components/chat/AskUserQuestionMenu.tsx +19 -14
- package/src/components/chat/AssistantMessage.tsx +18 -13
- package/src/components/chat/ChatInput.tsx +56 -40
- package/src/components/chat/ChatProjectBadge.tsx +27 -22
- package/src/components/chat/EmptyChatHero.tsx +2 -1
- package/src/components/chat/ExecPolicyPicker.tsx +12 -11
- package/src/components/chat/ExecutedActionsCard.tsx +45 -33
- package/src/components/chat/FilePathCode.tsx +8 -3
- package/src/components/chat/InterruptedTurnCard.tsx +4 -3
- package/src/components/chat/LocalChatPanel.tsx +2 -1
- package/src/components/chat/Markdown.tsx +2 -1
- package/src/components/chat/MessageList.tsx +3 -2
- package/src/components/chat/ModelPicker.tsx +25 -16
- package/src/components/chat/OrchestrationChildrenCard.tsx +2 -1
- package/src/components/chat/OrchestrationFlowCard.tsx +23 -22
- package/src/components/chat/PlanTodoList.tsx +2 -1
- package/src/components/chat/ProjectTrustBanner.tsx +19 -5
- package/src/components/chat/SessionOrchestrationFlow.tsx +27 -26
- package/src/components/chat/SessionTodoList.tsx +8 -7
- package/src/components/chat/SessionTreeModal.tsx +10 -9
- package/src/components/chat/StreamingStatus.tsx +4 -3
- package/src/components/chat/SuggestedReplies.tsx +3 -2
- package/src/components/chat/TaskOutcomeCards.tsx +8 -5
- package/src/components/chat/TaskPanelModal.tsx +33 -23
- package/src/components/chat/TaskProcessPanel.tsx +16 -15
- package/src/components/chat/TurnFilesCard.tsx +14 -13
- package/src/components/chat/UserMessage.tsx +12 -11
- package/src/components/chat/executed-actions-model.ts +47 -35
- package/src/components/chat/orchestration-flow-model.ts +16 -5
- package/src/components/chat/process-status.ts +8 -7
- package/src/components/chat/todo-list-model.ts +5 -4
- package/src/components/chat/turn-files.ts +8 -8
- package/src/components/chat/useChatTasks.ts +3 -3
- package/src/components/settings/AgentCapabilityFields.tsx +26 -23
- package/src/components/settings/AgentsSettingsPanel.tsx +33 -28
- package/src/components/settings/AppUpdateSettingsPanel.tsx +3 -2
- package/src/components/settings/AppearanceSettingsPanel.tsx +81 -37
- package/src/components/settings/DiagnoseSettingsPanel.tsx +11 -8
- package/src/components/settings/InsightsSettingsPanel.tsx +58 -43
- package/src/components/settings/LlmSettingsPanel.tsx +107 -86
- package/src/components/settings/McpSettingsPanel.tsx +72 -46
- package/src/components/settings/ModelIdCombobox.tsx +2 -1
- package/src/components/settings/OrchestrationSettingsPanel.tsx +8 -5
- package/src/components/settings/PortableSettingsPanel.tsx +83 -55
- package/src/components/settings/PythonRunnerSettingsPanel.tsx +26 -23
- package/src/components/settings/SecuritySettingsPanel.tsx +58 -34
- package/src/components/settings/SettingsNavMenu.tsx +3 -2
- package/src/components/settings/SettingsSaveButton.tsx +2 -1
- package/src/components/settings/SkillsSettingsPanel.tsx +43 -38
- package/src/components/settings/TelemetrySettingsPanel.tsx +20 -17
- package/src/components/settings/UsagePanel.tsx +22 -23
- package/src/components/settings/WebSearchSettingsPanel.tsx +28 -19
- package/src/components/settings/llm-vendors.ts +12 -7
- package/src/components/settings/model-capabilities.ts +16 -15
- package/src/components/settings/preset-choice-model.ts +6 -3
- package/src/fixtures/browser-dev-data.json +24 -24
- package/src/hooks/useChatsAndAgents.ts +9 -9
- package/src/hooks/useProjects.ts +3 -3
- package/src/i18n/agent-label.ts +15 -0
- package/src/i18n/index.tsx +68 -0
- package/src/i18n/runtime.ts +117 -0
- package/src/i18n/translate.ts +65 -0
- package/src/layouts/AgentLayout.tsx +13 -12
- package/src/lib/app-release.ts +16 -11
- package/src/lib/attachments.ts +18 -9
- package/src/lib/{browser-dev-electron-mock.ts → browser-dev-host-mock.ts} +42 -42
- package/src/lib/chat-title.ts +3 -2
- package/src/lib/chat-transport.ts +16 -12
- package/src/lib/date-groups.ts +23 -15
- package/src/lib/host-bridge.ts +371 -4
- package/src/lib/host-paste.ts +1 -1
- package/src/lib/http-bridge.ts +12 -9
- package/src/lib/insights.ts +2 -2
- package/src/lib/local-api.ts +5 -5
- package/src/lib/portable.ts +11 -10
- package/src/lib/show-thinking-content.ts +15 -3
- package/src/lib/slash-sources.ts +3 -3
- package/src/lib/tauri-bridge.ts +1 -1
- package/src/main.tsx +27 -4
- package/src/pages/AgentPage.tsx +39 -34
- package/src/pages/ChatPanelPreviewPage.tsx +46 -44
- package/src/pages/SettingsPage.tsx +51 -45
- package/src/vite-env.d.ts +3 -0
- package/vite.base.js +6 -2
- package/src/lib/electron-bridge.ts +0 -390
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import { LuChartBar } from 'react-icons/lu';
|
|
3
3
|
import { BRAND_NAME } from '@/brand';
|
|
4
|
-
import {
|
|
4
|
+
import { t } from '@/i18n';
|
|
5
|
+
import { getHostBridge, hasHostBridge } from '@/lib/host-bridge';
|
|
5
6
|
|
|
6
7
|
type InsightsWire = {
|
|
7
8
|
installId: string;
|
|
@@ -33,8 +34,8 @@ export function InsightsConsentBanner() {
|
|
|
33
34
|
const [saving, setSaving] = useState(false);
|
|
34
35
|
|
|
35
36
|
useEffect(() => {
|
|
36
|
-
if (!
|
|
37
|
-
void
|
|
37
|
+
if (!hasHostBridge()) return;
|
|
38
|
+
void getHostBridge()!
|
|
38
39
|
.localBackend.request<InsightsWire>({ method: 'GET', path: '/api/v2/local-settings/insights' })
|
|
39
40
|
.then((data) => {
|
|
40
41
|
if (!data.promptedAt) setOpen(true);
|
|
@@ -43,10 +44,10 @@ export function InsightsConsentBanner() {
|
|
|
43
44
|
}, []);
|
|
44
45
|
|
|
45
46
|
const save = useCallback(async (upload: boolean) => {
|
|
46
|
-
if (!
|
|
47
|
+
if (!hasHostBridge()) return;
|
|
47
48
|
setSaving(true);
|
|
48
49
|
try {
|
|
49
|
-
await
|
|
50
|
+
await getHostBridge()!.localBackend.request({
|
|
50
51
|
method: 'POST',
|
|
51
52
|
path: '/api/v2/local-settings/insights',
|
|
52
53
|
body: {
|
|
@@ -71,13 +72,13 @@ export function InsightsConsentBanner() {
|
|
|
71
72
|
className="flex-shrink-0 border-t border-agent-border bg-agent-muted px-2.5 py-2"
|
|
72
73
|
data-testid="insights-consent-banner"
|
|
73
74
|
role="region"
|
|
74
|
-
aria-label=
|
|
75
|
+
aria-label={t('Consent to help improve the product')}
|
|
75
76
|
>
|
|
76
77
|
<div className="flex items-center justify-between gap-3">
|
|
77
78
|
<div className="min-w-0">
|
|
78
|
-
<p className="text-xs font-medium text-agent-foreground"
|
|
79
|
+
<p className="text-xs font-medium text-agent-foreground">{t('Help improve {brand}', { brand: BRAND_NAME })}</p>
|
|
79
80
|
<p className="mt-1 text-xs leading-relaxed text-agent-muted-foreground">
|
|
80
|
-
|
|
81
|
+
{t('Agree to upload data to the server to help improve the product. You can change this later in Settings.')}
|
|
81
82
|
</p>
|
|
82
83
|
</div>
|
|
83
84
|
<div className="flex shrink-0 gap-2">
|
|
@@ -87,7 +88,7 @@ export function InsightsConsentBanner() {
|
|
|
87
88
|
onClick={() => void save(true)}
|
|
88
89
|
className="h-8 whitespace-nowrap rounded-full bg-agent-foreground px-3.5 text-xs text-agent-canvas disabled:opacity-50"
|
|
89
90
|
>
|
|
90
|
-
|
|
91
|
+
{t('Save')}
|
|
91
92
|
</button>
|
|
92
93
|
<button
|
|
93
94
|
type="button"
|
|
@@ -95,7 +96,7 @@ export function InsightsConsentBanner() {
|
|
|
95
96
|
onClick={() => void save(false)}
|
|
96
97
|
className="h-8 whitespace-nowrap rounded-full border border-agent-border bg-agent-canvas px-3.5 text-xs text-agent-foreground disabled:opacity-50"
|
|
97
98
|
>
|
|
98
|
-
|
|
99
|
+
{t('Cancel')}
|
|
99
100
|
</button>
|
|
100
101
|
</div>
|
|
101
102
|
</div>
|
|
@@ -111,11 +112,11 @@ export function InsightsSettingsPanel() {
|
|
|
111
112
|
const [error, setError] = useState<string | null>(null);
|
|
112
113
|
|
|
113
114
|
const load = useCallback(async () => {
|
|
114
|
-
if (!
|
|
115
|
+
if (!hasHostBridge()) return;
|
|
115
116
|
setLoading(true);
|
|
116
117
|
setError(null);
|
|
117
118
|
try {
|
|
118
|
-
const res = await
|
|
119
|
+
const res = await getHostBridge()!.localBackend.request<InsightsWire>({
|
|
119
120
|
method: 'GET',
|
|
120
121
|
path: '/api/v2/local-settings/insights',
|
|
121
122
|
});
|
|
@@ -132,11 +133,11 @@ export function InsightsSettingsPanel() {
|
|
|
132
133
|
}, [load]);
|
|
133
134
|
|
|
134
135
|
const save = async () => {
|
|
135
|
-
if (!
|
|
136
|
+
if (!hasHostBridge()) return;
|
|
136
137
|
setSaving(true);
|
|
137
138
|
setError(null);
|
|
138
139
|
try {
|
|
139
|
-
const res = await
|
|
140
|
+
const res = await getHostBridge()!.localBackend.request<InsightsWire>({
|
|
140
141
|
method: 'POST',
|
|
141
142
|
path: '/api/v2/local-settings/insights',
|
|
142
143
|
body: {
|
|
@@ -152,7 +153,7 @@ export function InsightsSettingsPanel() {
|
|
|
152
153
|
},
|
|
153
154
|
});
|
|
154
155
|
setData({ ...empty, ...res, profile: { ...empty.profile, ...res.profile } });
|
|
155
|
-
setStatus('
|
|
156
|
+
setStatus(t('Saved. Unchecked categories stay on this device only.'));
|
|
156
157
|
} catch (err) {
|
|
157
158
|
setError(err instanceof Error ? err.message : String(err));
|
|
158
159
|
} finally {
|
|
@@ -161,34 +162,38 @@ export function InsightsSettingsPanel() {
|
|
|
161
162
|
};
|
|
162
163
|
|
|
163
164
|
const exportFile = async () => {
|
|
164
|
-
if (!
|
|
165
|
+
if (!hasHostBridge()) return;
|
|
165
166
|
setError(null);
|
|
166
167
|
try {
|
|
167
|
-
const bundle = await
|
|
168
|
+
const bundle = await getHostBridge()!.localBackend.request<unknown>({
|
|
168
169
|
method: 'GET',
|
|
169
170
|
path: '/api/v2/insights/export',
|
|
170
171
|
});
|
|
171
172
|
const text = `${JSON.stringify(bundle, null, 2)}\n`;
|
|
172
|
-
const saved = await
|
|
173
|
-
title: '
|
|
173
|
+
const saved = await getHostBridge()!.local?.saveTextFile?.({
|
|
174
|
+
title: t('Export local insight records'),
|
|
174
175
|
defaultPath: `deeppath-insights-${new Date().toISOString().slice(0, 10)}.json`,
|
|
175
176
|
content: text,
|
|
176
177
|
});
|
|
177
|
-
if (saved?.canceled === false) setStatus(
|
|
178
|
+
if (saved?.canceled === false) setStatus(t('Saved to {path}', { path: saved.filePath ?? '' }));
|
|
178
179
|
} catch (err) {
|
|
179
180
|
setError(err instanceof Error ? err.message : String(err));
|
|
180
181
|
}
|
|
181
182
|
};
|
|
182
183
|
|
|
183
184
|
const uploadNow = async () => {
|
|
184
|
-
if (!
|
|
185
|
+
if (!hasHostBridge()) return;
|
|
185
186
|
setError(null);
|
|
186
187
|
try {
|
|
187
|
-
const res = await
|
|
188
|
+
const res = await getHostBridge()!.localBackend.request<{ ok: boolean; detail: string }>({
|
|
188
189
|
method: 'POST',
|
|
189
190
|
path: '/api/v2/insights/upload-local',
|
|
190
191
|
});
|
|
191
|
-
setStatus(
|
|
192
|
+
setStatus(
|
|
193
|
+
res.ok
|
|
194
|
+
? t('Uploaded to the {brand} server', { brand: BRAND_NAME })
|
|
195
|
+
: t('Upload failed. The records are still on this device. You can export a file and send it instead.'),
|
|
196
|
+
);
|
|
192
197
|
await load();
|
|
193
198
|
} catch (err) {
|
|
194
199
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -202,15 +207,17 @@ export function InsightsSettingsPanel() {
|
|
|
202
207
|
<div className="rounded-agent-md border border-agent-border/60 bg-agent-muted/30 p-2.5 space-y-2">
|
|
203
208
|
<h4 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
204
209
|
<LuChartBar className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
205
|
-
|
|
210
|
+
{t('Help improve the product (behavior / conversations / user info are confirmed separately)')}
|
|
206
211
|
</h4>
|
|
207
212
|
<p className="text-[11px] text-agent-muted-foreground">
|
|
208
|
-
|
|
213
|
+
{t(
|
|
214
|
+
'Even if the user does not agree to upload, records stay on this device. You can export JSON and send it to the product team, or click "Upload now" once.',
|
|
215
|
+
)}
|
|
209
216
|
</p>
|
|
210
|
-
{!
|
|
211
|
-
<p className="text-[11px] text-agent-muted-foreground"
|
|
217
|
+
{!hasHostBridge() ? (
|
|
218
|
+
<p className="text-[11px] text-agent-muted-foreground">{t('Open this in the desktop app')}</p>
|
|
212
219
|
) : loading ? (
|
|
213
|
-
<p className="text-[11px] text-agent-muted-foreground"
|
|
220
|
+
<p className="text-[11px] text-agent-muted-foreground">{t('Loading…')}</p>
|
|
214
221
|
) : (
|
|
215
222
|
<>
|
|
216
223
|
<label className="flex items-start gap-2 text-xs">
|
|
@@ -220,8 +227,8 @@ export function InsightsSettingsPanel() {
|
|
|
220
227
|
onChange={(e) => setData((d) => ({ ...d, shareBehavior: e.target.checked }))}
|
|
221
228
|
/>
|
|
222
229
|
<span>
|
|
223
|
-
|
|
224
|
-
<span className="block text-[10px] text-agent-muted-foreground"
|
|
230
|
+
{t('Auto-upload')} <strong>{t('behavior')}</strong>
|
|
231
|
+
<span className="block text-[10px] text-agent-muted-foreground">{t('Opens, sends, stuck settings, and so on. No question or answer text.')}</span>
|
|
225
232
|
</span>
|
|
226
233
|
</label>
|
|
227
234
|
<label className="flex items-start gap-2 text-xs">
|
|
@@ -231,8 +238,8 @@ export function InsightsSettingsPanel() {
|
|
|
231
238
|
onChange={(e) => setData((d) => ({ ...d, shareConversation: e.target.checked }))}
|
|
232
239
|
/>
|
|
233
240
|
<span>
|
|
234
|
-
|
|
235
|
-
<span className="block text-[10px] text-agent-muted-foreground"
|
|
241
|
+
{t('Auto-upload')} <strong>{t('conversations')}</strong>
|
|
242
|
+
<span className="block text-[10px] text-agent-muted-foreground">{t('Questions and answers (keys and user directories are redacted)')}</span>
|
|
236
243
|
</span>
|
|
237
244
|
</label>
|
|
238
245
|
<label className="flex items-start gap-2 text-xs">
|
|
@@ -242,21 +249,21 @@ export function InsightsSettingsPanel() {
|
|
|
242
249
|
onChange={(e) => setData((d) => ({ ...d, shareProfile: e.target.checked }))}
|
|
243
250
|
/>
|
|
244
251
|
<span>
|
|
245
|
-
|
|
246
|
-
<span className="block text-[10px] text-agent-muted-foreground"
|
|
252
|
+
{t('Auto-upload')} <strong>{t('user info')}</strong>
|
|
253
|
+
<span className="block text-[10px] text-agent-muted-foreground">{t('Separate from the two above. If unchecked, your name and email stay on this device only.')}</span>
|
|
247
254
|
</span>
|
|
248
255
|
</label>
|
|
249
256
|
<div className="grid grid-cols-2 gap-2">
|
|
250
257
|
{(
|
|
251
258
|
[
|
|
252
|
-
['displayName', '
|
|
253
|
-
['email', '
|
|
254
|
-
['company', '
|
|
255
|
-
['note', '
|
|
259
|
+
['displayName', 'Name'],
|
|
260
|
+
['email', 'Email'],
|
|
261
|
+
['company', 'Company/team'],
|
|
262
|
+
['note', 'Anything to tell us'],
|
|
256
263
|
] as const
|
|
257
264
|
).map(([key, label]) => (
|
|
258
265
|
<label key={key} className="space-y-1 text-[11px] text-agent-muted-foreground">
|
|
259
|
-
{label}
|
|
266
|
+
{t(label)}
|
|
260
267
|
<input
|
|
261
268
|
value={data.profile[key]}
|
|
262
269
|
onChange={(e) =>
|
|
@@ -268,7 +275,15 @@ export function InsightsSettingsPanel() {
|
|
|
268
275
|
))}
|
|
269
276
|
</div>
|
|
270
277
|
<p className="text-[10px] text-agent-muted-foreground">
|
|
271
|
-
|
|
278
|
+
{t(
|
|
279
|
+
'Recorded on this device: behavior {events} / conversations {turns} / profile {profile}, pending upload {pending}',
|
|
280
|
+
{
|
|
281
|
+
events: stats.events,
|
|
282
|
+
turns: stats.turns,
|
|
283
|
+
profile: stats.profile,
|
|
284
|
+
pending: stats.pending,
|
|
285
|
+
},
|
|
286
|
+
)}
|
|
272
287
|
{data.installId ? ` · ${data.installId.slice(0, 8)}` : ''}
|
|
273
288
|
</p>
|
|
274
289
|
<div className="flex flex-wrap gap-2">
|
|
@@ -278,21 +293,21 @@ export function InsightsSettingsPanel() {
|
|
|
278
293
|
onClick={() => void save()}
|
|
279
294
|
className="h-8 rounded-full bg-agent-foreground px-4 text-xs text-agent-canvas"
|
|
280
295
|
>
|
|
281
|
-
|
|
296
|
+
{t('Save')}
|
|
282
297
|
</button>
|
|
283
298
|
<button
|
|
284
299
|
type="button"
|
|
285
300
|
onClick={() => void exportFile()}
|
|
286
301
|
className="h-8 rounded-full border border-agent-border px-4 text-xs"
|
|
287
302
|
>
|
|
288
|
-
|
|
303
|
+
{t('Export a file for the developers')}
|
|
289
304
|
</button>
|
|
290
305
|
<button
|
|
291
306
|
type="button"
|
|
292
307
|
onClick={() => void uploadNow()}
|
|
293
308
|
className="h-8 rounded-full border border-agent-border px-4 text-xs"
|
|
294
309
|
>
|
|
295
|
-
|
|
310
|
+
{t('Upload local records now')}
|
|
296
311
|
</button>
|
|
297
312
|
</div>
|
|
298
313
|
</>
|