@data-club/ai-hub 0.0.4 → 0.0.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/dist/auth/locales/cs.d.ts.map +1 -1
- package/dist/auth/locales/cs.js +12 -0
- package/dist/auth/locales/cs.js.map +1 -1
- package/dist/auth/locales/sk.d.ts.map +1 -1
- package/dist/auth/locales/sk.js +12 -0
- package/dist/auth/locales/sk.js.map +1 -1
- package/dist/auth/texts.d.ts +18 -0
- package/dist/auth/texts.d.ts.map +1 -1
- package/dist/auth/texts.js +12 -0
- package/dist/auth/texts.js.map +1 -1
- package/dist/client/AIHubClient.d.ts +27 -0
- package/dist/client/AIHubClient.d.ts.map +1 -1
- package/dist/client/AIHubClient.js +18 -0
- package/dist/client/AIHubClient.js.map +1 -1
- package/dist/components/ChatShell/index.d.ts.map +1 -1
- package/dist/components/ChatShell/index.js +35 -2
- package/dist/components/ChatShell/index.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts +4 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts.map +1 -1
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js +6 -1
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts.map +1 -1
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js +6 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts.map +1 -1
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js +56 -1
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/auth/locales/cs.ts +15 -0
- package/src/auth/locales/sk.ts +15 -0
- package/src/auth/texts.ts +35 -0
- package/src/client/AIHubClient.ts +41 -0
- package/src/components/ChatShell/index.tsx +34 -2
- package/src/components/DataClubAIHubAdmin/RotatingMessageEditorModal.tsx +51 -0
- package/src/components/DataClubAIHubAdmin/RotatingMessagesPage.tsx +6 -0
- package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +145 -1
- package/src/types.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-club/ai-hub",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "React package for the data-club ai-hub: chat UI, login + change-password, admin UI, AIHub.* primitives, defineWebComponent helper, client SDK. See docs/SPECIFICATIONS.md §7.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
package/src/auth/locales/cs.ts
CHANGED
|
@@ -340,6 +340,12 @@ export const csTexts: AIHubTexts = {
|
|
|
340
340
|
rotatingModalCreateButton: "Vytvořit",
|
|
341
341
|
rotatingModalSaveButton: "Uložit",
|
|
342
342
|
rotatingModalSubmittingButton: "Ukládání…",
|
|
343
|
+
rotatingDisplaySectionLabel: "Zobrazené části výstupu",
|
|
344
|
+
rotatingDisplayHelper:
|
|
345
|
+
"Vyberte, které části připravené odpovědi agenta uživatelé uvidí. Skryté části se z Letty nemažou — jsou jen skryté v ai-hubu.",
|
|
346
|
+
rotatingDisplayNormalAlways: "Běžný výstup (vždy zobrazen)",
|
|
347
|
+
rotatingDisplayReasoning: "Uvažování (thinking)",
|
|
348
|
+
rotatingDisplayToolCalls: "Volání nástrojů",
|
|
343
349
|
/* Sessions overview. */
|
|
344
350
|
sessionsOverviewHeading: "Konverzace",
|
|
345
351
|
sessionsOverviewSearchPlaceholder: "Hledat v názvech…",
|
|
@@ -425,6 +431,15 @@ export const csTexts: AIHubTexts = {
|
|
|
425
431
|
settingsLanguageLockedHelper:
|
|
426
432
|
"Když je zapnuto, přepínač jazyka je skrytý a všichni uživatelé vidí výchozí jazyk. Když je vypnuto, uživatelé si mohou zvolit vlastní jazyk a jejich volba se zapamatuje na jejich zařízení.",
|
|
427
433
|
settingsSaveLanguageButton: "Uložit jazyk",
|
|
434
|
+
settingsAgentsSectionHeading: "Agenti",
|
|
435
|
+
settingsAgentsDefaultLabel: "Výchozí agent",
|
|
436
|
+
settingsAgentsDefaultNone: "Žádný — zobrazit seznam agentů",
|
|
437
|
+
settingsAgentsDefaultHelper:
|
|
438
|
+
"Když je nastaven, uživatelům se rovnou otevře tento agent místo výběru. Prostředí s jediným agentem vždy otevře tohoto jednoho agenta bez ohledu na toto nastavení.",
|
|
439
|
+
settingsAgentsSwitcherEnabledLabel: "Povolit uživatelům přepínat agenty",
|
|
440
|
+
settingsAgentsSwitcherEnabledHelper:
|
|
441
|
+
"Když je vypnuto, odkaz „← Agenti“ je skrytý a uživatelé zůstanou u svého agenta. Skrytý je také vždy, když existuje jen jeden agent.",
|
|
442
|
+
settingsSaveAgentsButton: "Uložit agenty",
|
|
428
443
|
settingsTogglesSectionHeading: "Přepínače funkcí UI",
|
|
429
444
|
settingsTogglesGroupSessions: "Panel konverzací",
|
|
430
445
|
settingsTogglesGroupInput: "Vstup",
|
package/src/auth/locales/sk.ts
CHANGED
|
@@ -340,6 +340,12 @@ export const skTexts: AIHubTexts = {
|
|
|
340
340
|
rotatingModalCreateButton: "Vytvoriť",
|
|
341
341
|
rotatingModalSaveButton: "Uložiť",
|
|
342
342
|
rotatingModalSubmittingButton: "Ukladanie…",
|
|
343
|
+
rotatingDisplaySectionLabel: "Zobrazené časti výstupu",
|
|
344
|
+
rotatingDisplayHelper:
|
|
345
|
+
"Vyberte, ktoré časti pripravenej odpovede agenta používatelia uvidia. Skryté časti sa z Letty nemažú — sú len skryté v ai-hube.",
|
|
346
|
+
rotatingDisplayNormalAlways: "Bežný výstup (vždy zobrazený)",
|
|
347
|
+
rotatingDisplayReasoning: "Uvažovanie (thinking)",
|
|
348
|
+
rotatingDisplayToolCalls: "Volania nástrojov",
|
|
343
349
|
/* Sessions overview. */
|
|
344
350
|
sessionsOverviewHeading: "Konverzácie",
|
|
345
351
|
sessionsOverviewSearchPlaceholder: "Hľadať v názvoch…",
|
|
@@ -425,6 +431,15 @@ export const skTexts: AIHubTexts = {
|
|
|
425
431
|
settingsLanguageLockedHelper:
|
|
426
432
|
"Keď je zapnuté, prepínač jazyka je skrytý a všetci používatelia vidia predvolený jazyk. Keď je vypnuté, používatelia si môžu zvoliť vlastný jazyk a ich voľba sa zapamätá na ich zariadení.",
|
|
427
433
|
settingsSaveLanguageButton: "Uložiť jazyk",
|
|
434
|
+
settingsAgentsSectionHeading: "Agenti",
|
|
435
|
+
settingsAgentsDefaultLabel: "Predvolený agent",
|
|
436
|
+
settingsAgentsDefaultNone: "Žiadny — zobraziť zoznam agentov",
|
|
437
|
+
settingsAgentsDefaultHelper:
|
|
438
|
+
"Keď je nastavený, používateľom sa rovno otvorí tento agent namiesto výberu. Prostredie s jediným agentom vždy otvorí tohto jedného agenta bez ohľadu na toto nastavenie.",
|
|
439
|
+
settingsAgentsSwitcherEnabledLabel: "Povoliť používateľom prepínať agentov",
|
|
440
|
+
settingsAgentsSwitcherEnabledHelper:
|
|
441
|
+
"Keď je vypnuté, odkaz „← Agenti“ je skrytý a používatelia zostanú pri svojom agentovi. Skrytý je tiež vždy, keď existuje iba jeden agent.",
|
|
442
|
+
settingsSaveAgentsButton: "Uložiť agentov",
|
|
428
443
|
settingsTogglesSectionHeading: "Prepínače funkcií UI",
|
|
429
444
|
settingsTogglesGroupSessions: "Panel konverzácií",
|
|
430
445
|
settingsTogglesGroupInput: "Vstup",
|
package/src/auth/texts.ts
CHANGED
|
@@ -688,6 +688,16 @@ export interface AIHubAdminTexts {
|
|
|
688
688
|
rotatingModalSaveButton: string;
|
|
689
689
|
/** In-flight submit button label. */
|
|
690
690
|
rotatingModalSubmittingButton: string;
|
|
691
|
+
/** Heading for the "which output parts to display" group in the editor. */
|
|
692
|
+
rotatingDisplaySectionLabel: string;
|
|
693
|
+
/** Helper under the group — explains hide-not-delete. */
|
|
694
|
+
rotatingDisplayHelper: string;
|
|
695
|
+
/** Always-on row label for normal assistant text. */
|
|
696
|
+
rotatingDisplayNormalAlways: string;
|
|
697
|
+
/** Toggle label — show the agent's reasoning / thinking. */
|
|
698
|
+
rotatingDisplayReasoning: string;
|
|
699
|
+
/** Toggle label — show tool calls. */
|
|
700
|
+
rotatingDisplayToolCalls: string;
|
|
691
701
|
|
|
692
702
|
/* ── Step 12 — Sessions overview ([07-admin-sessions-overview.md]) ─ */
|
|
693
703
|
/** Page heading. */
|
|
@@ -814,6 +824,16 @@ export interface AIHubAdminTexts {
|
|
|
814
824
|
settingsLanguageLockedHelper: string;
|
|
815
825
|
settingsSaveLanguageButton: string;
|
|
816
826
|
|
|
827
|
+
/* Agents section. */
|
|
828
|
+
settingsAgentsSectionHeading: string;
|
|
829
|
+
settingsAgentsDefaultLabel: string;
|
|
830
|
+
/** First `<option>` — no default agent (show the picker). */
|
|
831
|
+
settingsAgentsDefaultNone: string;
|
|
832
|
+
settingsAgentsDefaultHelper: string;
|
|
833
|
+
settingsAgentsSwitcherEnabledLabel: string;
|
|
834
|
+
settingsAgentsSwitcherEnabledHelper: string;
|
|
835
|
+
settingsSaveAgentsButton: string;
|
|
836
|
+
|
|
817
837
|
/* Feature toggles section. */
|
|
818
838
|
settingsTogglesSectionHeading: string;
|
|
819
839
|
settingsTogglesGroupSessions: string;
|
|
@@ -1194,6 +1214,12 @@ export const defaultAIHubTexts: AIHubTexts = Object.freeze({
|
|
|
1194
1214
|
rotatingModalCreateButton: "Create",
|
|
1195
1215
|
rotatingModalSaveButton: "Save",
|
|
1196
1216
|
rotatingModalSubmittingButton: "Saving…",
|
|
1217
|
+
rotatingDisplaySectionLabel: "Displayed output parts",
|
|
1218
|
+
rotatingDisplayHelper:
|
|
1219
|
+
"Choose which parts of the agent's prepared reply users see. Hidden parts are not deleted from Letta — only hidden in ai-hub.",
|
|
1220
|
+
rotatingDisplayNormalAlways: "Normal output (always shown)",
|
|
1221
|
+
rotatingDisplayReasoning: "Thinking (reasoning)",
|
|
1222
|
+
rotatingDisplayToolCalls: "Tool calls",
|
|
1197
1223
|
/* Step 12 — Sessions overview. */
|
|
1198
1224
|
sessionsOverviewHeading: "Sessions",
|
|
1199
1225
|
sessionsOverviewSearchPlaceholder: "Search titles…",
|
|
@@ -1280,6 +1306,15 @@ export const defaultAIHubTexts: AIHubTexts = Object.freeze({
|
|
|
1280
1306
|
settingsLanguageLockedHelper:
|
|
1281
1307
|
"When on, the language switcher is hidden and every user sees the default language. When off, users can pick their own language and their choice is remembered on their device.",
|
|
1282
1308
|
settingsSaveLanguageButton: "Save language",
|
|
1309
|
+
settingsAgentsSectionHeading: "Agents",
|
|
1310
|
+
settingsAgentsDefaultLabel: "Default agent",
|
|
1311
|
+
settingsAgentsDefaultNone: "No default — show the agent list",
|
|
1312
|
+
settingsAgentsDefaultHelper:
|
|
1313
|
+
"When set, users open straight into this agent instead of the picker. A deployment with a single agent always opens that one regardless of this setting.",
|
|
1314
|
+
settingsAgentsSwitcherEnabledLabel: "Allow users to switch agents",
|
|
1315
|
+
settingsAgentsSwitcherEnabledHelper:
|
|
1316
|
+
"When off, the “← Agents” back link is hidden so users stay on their agent. It is also hidden whenever only one agent exists.",
|
|
1317
|
+
settingsSaveAgentsButton: "Save agents",
|
|
1283
1318
|
settingsTogglesSectionHeading: "UI feature toggles",
|
|
1284
1319
|
settingsTogglesGroupSessions: "Sessions panel",
|
|
1285
1320
|
settingsTogglesGroupInput: "Input",
|
|
@@ -428,6 +428,14 @@ export interface AdminRotatingMessageView {
|
|
|
428
428
|
next_rotation_at: string;
|
|
429
429
|
/** Soft-hide flag. */
|
|
430
430
|
is_archived: boolean;
|
|
431
|
+
/**
|
|
432
|
+
* Whether the agent's reasoning is shown in the seeded opening of sessions
|
|
433
|
+
* forked from this rotating message. When false, ai-hub hides it at display
|
|
434
|
+
* time (not deleted from Letta). Normal assistant text is always shown.
|
|
435
|
+
*/
|
|
436
|
+
reasoning_visible: boolean;
|
|
437
|
+
/** Same, for the seeded opening's tool calls. */
|
|
438
|
+
tool_calls_visible: boolean;
|
|
431
439
|
/** ISO-8601 timestamp. */
|
|
432
440
|
created_at: string;
|
|
433
441
|
/** ISO-8601 timestamp. */
|
|
@@ -446,6 +454,10 @@ export interface CreateAdminRotatingMessageBody {
|
|
|
446
454
|
dailyTime?: string | null;
|
|
447
455
|
/** Required when `scheduleKind === "cron"` (5-field cron). */
|
|
448
456
|
cronExpression?: string | null;
|
|
457
|
+
/** Show reasoning in the seeded opening. Defaults to true server-side. */
|
|
458
|
+
reasoningVisible?: boolean;
|
|
459
|
+
/** Show tool calls in the seeded opening. Defaults to true server-side. */
|
|
460
|
+
toolCallsVisible?: boolean;
|
|
449
461
|
}
|
|
450
462
|
|
|
451
463
|
/** Body for `patchAdminRotatingMessage`. Each field independently optional. */
|
|
@@ -458,6 +470,8 @@ export interface PatchAdminRotatingMessageBody {
|
|
|
458
470
|
dailyTime?: string | null;
|
|
459
471
|
cronExpression?: string | null;
|
|
460
472
|
isArchived?: boolean;
|
|
473
|
+
reasoningVisible?: boolean;
|
|
474
|
+
toolCallsVisible?: boolean;
|
|
461
475
|
}
|
|
462
476
|
|
|
463
477
|
/** Response from `rotateAdminRotatingMessageNow`. */
|
|
@@ -583,6 +597,13 @@ export interface AdminSettingsView {
|
|
|
583
597
|
default: AIHubLanguage;
|
|
584
598
|
user_locked: boolean;
|
|
585
599
|
};
|
|
600
|
+
/** Agent-picker settings, stored as separate columns server-side. */
|
|
601
|
+
agents: {
|
|
602
|
+
/** Default ai-hub agent id auto-opened on load, or `null` for the picker. */
|
|
603
|
+
default_id: string | null;
|
|
604
|
+
/** Whether users may switch agents (the "← Agents" back link). */
|
|
605
|
+
switcher_enabled: boolean;
|
|
606
|
+
};
|
|
586
607
|
/** The flat §8.4 feature-toggles blob. */
|
|
587
608
|
feature_toggles: AIHubFeatureToggles;
|
|
588
609
|
/** ISO-8601 of the last signing-key rotation. `null` until first rotation. */
|
|
@@ -611,6 +632,12 @@ export interface PatchAdminSettingsBody {
|
|
|
611
632
|
default?: AIHubLanguage;
|
|
612
633
|
userLocked?: boolean;
|
|
613
634
|
};
|
|
635
|
+
/** Agent-picker partial — either or both fields. */
|
|
636
|
+
agents?: {
|
|
637
|
+
/** Agent id to default to; `null` / `""` clears (→ show the picker). */
|
|
638
|
+
defaultId?: string | null;
|
|
639
|
+
switcherEnabled?: boolean;
|
|
640
|
+
};
|
|
614
641
|
}
|
|
615
642
|
|
|
616
643
|
/** Response from `rotateAdminMetricsToken` — the new token returned **once**. */
|
|
@@ -1330,6 +1357,8 @@ export class AIHubClient {
|
|
|
1330
1357
|
if (body.intervalSeconds !== undefined) wire.interval_seconds = body.intervalSeconds;
|
|
1331
1358
|
if (body.dailyTime !== undefined) wire.daily_time = body.dailyTime;
|
|
1332
1359
|
if (body.cronExpression !== undefined) wire.cron_expression = body.cronExpression;
|
|
1360
|
+
if (body.reasoningVisible !== undefined) wire.reasoning_visible = body.reasoningVisible;
|
|
1361
|
+
if (body.toolCallsVisible !== undefined) wire.tool_calls_visible = body.toolCallsVisible;
|
|
1333
1362
|
return this.requestJson<AdminRotatingMessageView>(
|
|
1334
1363
|
"POST",
|
|
1335
1364
|
"/admin/rotating-messages",
|
|
@@ -1360,6 +1389,8 @@ export class AIHubClient {
|
|
|
1360
1389
|
if (body.dailyTime !== undefined) wire.daily_time = body.dailyTime;
|
|
1361
1390
|
if (body.cronExpression !== undefined) wire.cron_expression = body.cronExpression;
|
|
1362
1391
|
if (body.isArchived !== undefined) wire.is_archived = body.isArchived;
|
|
1392
|
+
if (body.reasoningVisible !== undefined) wire.reasoning_visible = body.reasoningVisible;
|
|
1393
|
+
if (body.toolCallsVisible !== undefined) wire.tool_calls_visible = body.toolCallsVisible;
|
|
1363
1394
|
return this.requestJson<AdminRotatingMessageView>(
|
|
1364
1395
|
"PATCH",
|
|
1365
1396
|
`/admin/rotating-messages/${encodeURIComponent(id)}`,
|
|
@@ -1505,6 +1536,16 @@ export class AIHubClient {
|
|
|
1505
1536
|
}
|
|
1506
1537
|
wire.language = languageWire;
|
|
1507
1538
|
}
|
|
1539
|
+
if (body.agents !== undefined) {
|
|
1540
|
+
const agentsWire: Record<string, unknown> = {};
|
|
1541
|
+
if (body.agents.defaultId !== undefined) {
|
|
1542
|
+
agentsWire.default_id = body.agents.defaultId;
|
|
1543
|
+
}
|
|
1544
|
+
if (body.agents.switcherEnabled !== undefined) {
|
|
1545
|
+
agentsWire.switcher_enabled = body.agents.switcherEnabled;
|
|
1546
|
+
}
|
|
1547
|
+
wire.agents = agentsWire;
|
|
1548
|
+
}
|
|
1508
1549
|
return this.requestJson<AdminSettingsView>("PATCH", "/admin/settings", {
|
|
1509
1550
|
body: wire,
|
|
1510
1551
|
});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* @see ui-sketches/00-shell.md, ui-sketches/03-chat.md
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { useEffect, useMemo, type ReactNode } from "react";
|
|
16
|
+
import { useEffect, useMemo, useRef, type ReactNode } from "react";
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
19
|
type AIHubTexts,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../../auth/texts.js";
|
|
22
22
|
import { useResolvedTexts } from "../../context/useAIHubTexts.js";
|
|
23
23
|
import { useAIHub } from "../../context/useAIHub.js";
|
|
24
|
+
import { useAIHubAuth } from "../../context/useAIHubAuth.js";
|
|
24
25
|
import {
|
|
25
26
|
resolveAffordanceFlags,
|
|
26
27
|
type AIHubAffordanceFlags,
|
|
@@ -171,9 +172,13 @@ export function ChatShell(props: ChatShellProps): ReactNode {
|
|
|
171
172
|
setActiveSession,
|
|
172
173
|
featureToggles,
|
|
173
174
|
} = useAIHub();
|
|
175
|
+
const { deployment } = useAIHubAuth();
|
|
174
176
|
const texts = useResolvedTexts(props.texts);
|
|
175
177
|
const productName = props.productName ?? "ai-hub";
|
|
176
178
|
const showAgentsPicker = props.showAgentsPicker ?? true;
|
|
179
|
+
// §7.x agent-picker deployment config: when switching is off the "← Agents"
|
|
180
|
+
// back link is hidden; a single-agent deployment hides it too (see below).
|
|
181
|
+
const agentSwitcherEnabled = deployment?.agent_switcher_enabled ?? true;
|
|
177
182
|
const sessionsPanelPosition = props.sessionsPanelPosition ?? "left";
|
|
178
183
|
const sessionsPanelWidthPercent = props.sessionsPanelWidthPercent ?? 25;
|
|
179
184
|
|
|
@@ -214,6 +219,31 @@ export function ChatShell(props: ChatShellProps): ReactNode {
|
|
|
214
219
|
}
|
|
215
220
|
}, [autoSelectOnlyAgent, agents, setActiveAgent]);
|
|
216
221
|
|
|
222
|
+
// Deployment default agent (multi-agent deployments): open it once on first
|
|
223
|
+
// agents-load instead of showing the picker. A deep-link `initialAgentId`
|
|
224
|
+
// wins (handled by its own effect above). Guarded by a ref so that, when
|
|
225
|
+
// switching is enabled, clicking "← Agents" genuinely returns to the picker
|
|
226
|
+
// rather than snapping straight back to the default.
|
|
227
|
+
const appliedDefaultAgentRef = useRef(false);
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (appliedDefaultAgentRef.current) return;
|
|
230
|
+
if (props.initialAgentId) return;
|
|
231
|
+
if (agentsLoading || agents.length <= 1) return;
|
|
232
|
+
appliedDefaultAgentRef.current = true;
|
|
233
|
+
if (activeAgentId) return;
|
|
234
|
+
const defaultId = deployment?.default_agent_id ?? null;
|
|
235
|
+
if (defaultId && agents.some((a) => a.id === defaultId)) {
|
|
236
|
+
setActiveAgent(defaultId);
|
|
237
|
+
}
|
|
238
|
+
}, [
|
|
239
|
+
agents,
|
|
240
|
+
agentsLoading,
|
|
241
|
+
activeAgentId,
|
|
242
|
+
props.initialAgentId,
|
|
243
|
+
deployment,
|
|
244
|
+
setActiveAgent,
|
|
245
|
+
]);
|
|
246
|
+
|
|
217
247
|
const inPicker = showAgentsPicker && !activeAgentId;
|
|
218
248
|
|
|
219
249
|
const rootClass = props.className
|
|
@@ -259,7 +289,9 @@ export function ChatShell(props: ChatShellProps): ReactNode {
|
|
|
259
289
|
/>
|
|
260
290
|
) : null}
|
|
261
291
|
<MainColumn
|
|
262
|
-
showBackToAgents={
|
|
292
|
+
showBackToAgents={
|
|
293
|
+
showAgentsPicker && agentSwitcherEnabled && agents.length > 1
|
|
294
|
+
}
|
|
263
295
|
showTurnStatus={flags.turnStatusVisible}
|
|
264
296
|
{...(props.texts !== undefined ? { texts: props.texts } : {})}
|
|
265
297
|
{...(props.customHeaderContent !== undefined
|
|
@@ -33,6 +33,8 @@ export interface RotatingEditorInitial {
|
|
|
33
33
|
intervalSeconds: number | null;
|
|
34
34
|
dailyTime: string | null;
|
|
35
35
|
cronExpression: string | null;
|
|
36
|
+
reasoningVisible: boolean;
|
|
37
|
+
toolCallsVisible: boolean;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/** Submit body passed up to the parent page. */
|
|
@@ -44,6 +46,8 @@ export interface RotatingEditorSubmitBody {
|
|
|
44
46
|
intervalSeconds: number | null;
|
|
45
47
|
dailyTime: string | null;
|
|
46
48
|
cronExpression: string | null;
|
|
49
|
+
reasoningVisible: boolean;
|
|
50
|
+
toolCallsVisible: boolean;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/** Props for `<RotatingMessageEditorModal />`. */
|
|
@@ -90,6 +94,13 @@ export function RotatingMessageEditorModal(
|
|
|
90
94
|
const [intervalUnit, setIntervalUnit] = useState<"hours" | "days">(initialInterval.unit);
|
|
91
95
|
const [dailyTime, setDailyTime] = useState(props.initial?.dailyTime ?? "06:00");
|
|
92
96
|
const [cronExpression, setCronExpression] = useState(props.initial?.cronExpression ?? "0 6 * * *");
|
|
97
|
+
// Which output parts the seeded opening shows. Normal text is always shown.
|
|
98
|
+
const [reasoningVisible, setReasoningVisible] = useState(
|
|
99
|
+
props.initial?.reasoningVisible ?? true,
|
|
100
|
+
);
|
|
101
|
+
const [toolCallsVisible, setToolCallsVisible] = useState(
|
|
102
|
+
props.initial?.toolCallsVisible ?? true,
|
|
103
|
+
);
|
|
93
104
|
|
|
94
105
|
const [nameError, setNameError] = useState<string | null>(null);
|
|
95
106
|
const [promptError, setPromptError] = useState<string | null>(null);
|
|
@@ -172,6 +183,8 @@ export function RotatingMessageEditorModal(
|
|
|
172
183
|
prompt: trimmedPrompt,
|
|
173
184
|
lettaAgentId: trimmedAgent,
|
|
174
185
|
...schedule.body,
|
|
186
|
+
reasoningVisible,
|
|
187
|
+
toolCallsVisible,
|
|
175
188
|
});
|
|
176
189
|
} catch (err) {
|
|
177
190
|
if (err instanceof AIHubRequestError) {
|
|
@@ -377,6 +390,44 @@ export function RotatingMessageEditorModal(
|
|
|
377
390
|
) : null}
|
|
378
391
|
</fieldset>
|
|
379
392
|
|
|
393
|
+
<fieldset className="data-club-ai-hub-admin__radio-group">
|
|
394
|
+
<legend className="data-club-ai-hub-admin__form-label">
|
|
395
|
+
{props.texts.rotatingDisplaySectionLabel}
|
|
396
|
+
</legend>
|
|
397
|
+
<label className="data-club-ai-hub-admin__checkbox">
|
|
398
|
+
<input
|
|
399
|
+
type="checkbox"
|
|
400
|
+
checked
|
|
401
|
+
disabled
|
|
402
|
+
data-testid="admin-rotating-display-normal"
|
|
403
|
+
/>
|
|
404
|
+
<span>{props.texts.rotatingDisplayNormalAlways}</span>
|
|
405
|
+
</label>
|
|
406
|
+
<label className="data-club-ai-hub-admin__checkbox">
|
|
407
|
+
<input
|
|
408
|
+
type="checkbox"
|
|
409
|
+
data-testid="admin-rotating-display-reasoning"
|
|
410
|
+
checked={reasoningVisible}
|
|
411
|
+
onChange={(event) => setReasoningVisible(event.target.checked)}
|
|
412
|
+
disabled={submitting}
|
|
413
|
+
/>
|
|
414
|
+
<span>{props.texts.rotatingDisplayReasoning}</span>
|
|
415
|
+
</label>
|
|
416
|
+
<label className="data-club-ai-hub-admin__checkbox">
|
|
417
|
+
<input
|
|
418
|
+
type="checkbox"
|
|
419
|
+
data-testid="admin-rotating-display-tool-calls"
|
|
420
|
+
checked={toolCallsVisible}
|
|
421
|
+
onChange={(event) => setToolCallsVisible(event.target.checked)}
|
|
422
|
+
disabled={submitting}
|
|
423
|
+
/>
|
|
424
|
+
<span>{props.texts.rotatingDisplayToolCalls}</span>
|
|
425
|
+
</label>
|
|
426
|
+
<span className="data-club-ai-hub-admin__form-helper">
|
|
427
|
+
{props.texts.rotatingDisplayHelper}
|
|
428
|
+
</span>
|
|
429
|
+
</fieldset>
|
|
430
|
+
|
|
380
431
|
{banner !== null ? (
|
|
381
432
|
<div className="data-club-ai-hub-admin__error" role="alert" data-testid="admin-rotating-modal-banner">
|
|
382
433
|
{banner}
|
|
@@ -135,6 +135,8 @@ export function AdminRotatingMessagesPage(
|
|
|
135
135
|
intervalSeconds: body.intervalSeconds,
|
|
136
136
|
dailyTime: body.dailyTime,
|
|
137
137
|
cronExpression: body.cronExpression,
|
|
138
|
+
reasoningVisible: body.reasoningVisible,
|
|
139
|
+
toolCallsVisible: body.toolCallsVisible,
|
|
138
140
|
});
|
|
139
141
|
upsert(created);
|
|
140
142
|
setEditor({ kind: "closed" });
|
|
@@ -152,6 +154,8 @@ export function AdminRotatingMessagesPage(
|
|
|
152
154
|
intervalSeconds: body.intervalSeconds,
|
|
153
155
|
dailyTime: body.dailyTime,
|
|
154
156
|
cronExpression: body.cronExpression,
|
|
157
|
+
reasoningVisible: body.reasoningVisible,
|
|
158
|
+
toolCallsVisible: body.toolCallsVisible,
|
|
155
159
|
});
|
|
156
160
|
upsert(updated);
|
|
157
161
|
setEditor({ kind: "closed" });
|
|
@@ -362,6 +366,8 @@ export function AdminRotatingMessagesPage(
|
|
|
362
366
|
intervalSeconds: editor.row.interval_seconds,
|
|
363
367
|
dailyTime: editor.row.daily_time,
|
|
364
368
|
cronExpression: editor.row.cron_expression,
|
|
369
|
+
reasoningVisible: editor.row.reasoning_visible,
|
|
370
|
+
toolCallsVisible: editor.row.tool_calls_visible,
|
|
365
371
|
}}
|
|
366
372
|
texts={props.texts}
|
|
367
373
|
onSubmit={(body) => handleEditSubmit(editor.row, body)}
|
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
import { useEffect, useState, type ReactNode } from "react";
|
|
28
28
|
|
|
29
29
|
import type { AIHubAdminTexts, AIHubLanguage } from "../../auth/texts.js";
|
|
30
|
-
import type {
|
|
30
|
+
import type {
|
|
31
|
+
AdminAgentView,
|
|
32
|
+
AdminSettingsView,
|
|
33
|
+
} from "../../client/AIHubClient.js";
|
|
31
34
|
import { useAIHub } from "../../context/useAIHub.js";
|
|
32
35
|
import { useAIHubAuth } from "../../context/useAIHubAuth.js";
|
|
33
36
|
import {
|
|
@@ -188,6 +191,11 @@ export function AdminSettingsPage(props: AdminSettingsPageProps): ReactNode {
|
|
|
188
191
|
texts={props.texts}
|
|
189
192
|
onSaved={handleSectionSaved}
|
|
190
193
|
/>
|
|
194
|
+
<AgentsSection
|
|
195
|
+
settings={settings}
|
|
196
|
+
texts={props.texts}
|
|
197
|
+
onSaved={handleSectionSaved}
|
|
198
|
+
/>
|
|
191
199
|
<FeatureTogglesSection
|
|
192
200
|
settings={settings}
|
|
193
201
|
texts={props.texts}
|
|
@@ -646,6 +654,142 @@ function LanguageSection(props: LanguageSectionProps): ReactNode {
|
|
|
646
654
|
);
|
|
647
655
|
}
|
|
648
656
|
|
|
657
|
+
/* ────────────────────────────────────────────────────────────────────────── */
|
|
658
|
+
/* Section: Agents */
|
|
659
|
+
/* ────────────────────────────────────────────────────────────────────────── */
|
|
660
|
+
|
|
661
|
+
interface AgentsSectionProps extends SectionProps {
|
|
662
|
+
onSaved: (next: AdminSettingsView) => void;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function AgentsSection(props: AgentsSectionProps): ReactNode {
|
|
666
|
+
const { client } = useAIHub();
|
|
667
|
+
const [defaultAgentId, setDefaultAgentId] = useState<string>(
|
|
668
|
+
props.settings.agents.default_id ?? "",
|
|
669
|
+
);
|
|
670
|
+
const [switcherEnabled, setSwitcherEnabled] = useState(
|
|
671
|
+
props.settings.agents.switcher_enabled,
|
|
672
|
+
);
|
|
673
|
+
// Non-archived agents to populate the default-agent <select>.
|
|
674
|
+
const [agents, setAgents] = useState<AdminAgentView[] | null>(null);
|
|
675
|
+
const [saving, setSaving] = useState(false);
|
|
676
|
+
const [flash, setFlash] = useState<{ tone: "success" | "error"; text: string } | null>(
|
|
677
|
+
null,
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
useEffect(() => {
|
|
681
|
+
let cancelled = false;
|
|
682
|
+
void (async () => {
|
|
683
|
+
try {
|
|
684
|
+
const rows = await client.listAdminAgents();
|
|
685
|
+
if (cancelled) return;
|
|
686
|
+
setAgents(rows.filter((a) => !a.is_archived));
|
|
687
|
+
} catch {
|
|
688
|
+
if (!cancelled) setAgents([]);
|
|
689
|
+
}
|
|
690
|
+
})();
|
|
691
|
+
return () => {
|
|
692
|
+
cancelled = true;
|
|
693
|
+
};
|
|
694
|
+
}, [client]);
|
|
695
|
+
|
|
696
|
+
async function handleSave(event: React.FormEvent): Promise<void> {
|
|
697
|
+
event.preventDefault();
|
|
698
|
+
setSaving(true);
|
|
699
|
+
setFlash(null);
|
|
700
|
+
try {
|
|
701
|
+
const next = await client.patchAdminSettings({
|
|
702
|
+
agents: { defaultId: defaultAgentId, switcherEnabled },
|
|
703
|
+
});
|
|
704
|
+
props.onSaved(next);
|
|
705
|
+
setFlash({ tone: "success", text: props.texts.settingsSaveSuccessFlash });
|
|
706
|
+
} catch (err) {
|
|
707
|
+
setFlash({
|
|
708
|
+
tone: "error",
|
|
709
|
+
text:
|
|
710
|
+
err instanceof Error && err.message.length > 0
|
|
711
|
+
? err.message
|
|
712
|
+
: props.texts.settingsSaveGenericError,
|
|
713
|
+
});
|
|
714
|
+
} finally {
|
|
715
|
+
setSaving(false);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return (
|
|
720
|
+
<section
|
|
721
|
+
className="data-club-ai-hub-admin__settings-section"
|
|
722
|
+
data-testid="admin-settings-agents-section"
|
|
723
|
+
>
|
|
724
|
+
<h2 className="data-club-ai-hub-admin__settings-section-heading">
|
|
725
|
+
{props.texts.settingsAgentsSectionHeading}
|
|
726
|
+
</h2>
|
|
727
|
+
<form onSubmit={(event) => void handleSave(event)} noValidate>
|
|
728
|
+
<label
|
|
729
|
+
className="data-club-ai-hub-admin__form-label"
|
|
730
|
+
htmlFor="admin-settings-default-agent"
|
|
731
|
+
>
|
|
732
|
+
{props.texts.settingsAgentsDefaultLabel}
|
|
733
|
+
</label>
|
|
734
|
+
<select
|
|
735
|
+
id="admin-settings-default-agent"
|
|
736
|
+
className="data-club-ai-hub-admin__form-input"
|
|
737
|
+
data-testid="admin-settings-default-agent"
|
|
738
|
+
value={defaultAgentId}
|
|
739
|
+
onChange={(event) => setDefaultAgentId(event.target.value)}
|
|
740
|
+
disabled={saving || agents === null}
|
|
741
|
+
>
|
|
742
|
+
<option value="">{props.texts.settingsAgentsDefaultNone}</option>
|
|
743
|
+
{(agents ?? []).map((agent) => (
|
|
744
|
+
<option key={agent.id} value={agent.id}>
|
|
745
|
+
{agent.display_name}
|
|
746
|
+
</option>
|
|
747
|
+
))}
|
|
748
|
+
</select>
|
|
749
|
+
<p className="data-club-ai-hub-admin__form-helper">
|
|
750
|
+
{props.texts.settingsAgentsDefaultHelper}
|
|
751
|
+
</p>
|
|
752
|
+
<label className="data-club-ai-hub-admin__checkbox">
|
|
753
|
+
<input
|
|
754
|
+
type="checkbox"
|
|
755
|
+
data-testid="admin-settings-agent-switcher-enabled"
|
|
756
|
+
checked={switcherEnabled}
|
|
757
|
+
onChange={(event) => setSwitcherEnabled(event.target.checked)}
|
|
758
|
+
disabled={saving}
|
|
759
|
+
/>
|
|
760
|
+
<span>{props.texts.settingsAgentsSwitcherEnabledLabel}</span>
|
|
761
|
+
</label>
|
|
762
|
+
<p className="data-club-ai-hub-admin__form-helper">
|
|
763
|
+
{props.texts.settingsAgentsSwitcherEnabledHelper}
|
|
764
|
+
</p>
|
|
765
|
+
{flash !== null ? (
|
|
766
|
+
<div
|
|
767
|
+
className={
|
|
768
|
+
flash.tone === "success"
|
|
769
|
+
? "data-club-ai-hub-admin__flash data-club-ai-hub-admin__flash--success"
|
|
770
|
+
: "data-club-ai-hub-admin__flash data-club-ai-hub-admin__flash--error"
|
|
771
|
+
}
|
|
772
|
+
data-testid="admin-settings-agents-flash"
|
|
773
|
+
role={flash.tone === "error" ? "alert" : "status"}
|
|
774
|
+
>
|
|
775
|
+
{flash.text}
|
|
776
|
+
</div>
|
|
777
|
+
) : null}
|
|
778
|
+
<button
|
|
779
|
+
type="submit"
|
|
780
|
+
className="data-club-ai-hub-admin__button data-club-ai-hub-admin__button--primary"
|
|
781
|
+
data-testid="admin-settings-agents-save"
|
|
782
|
+
disabled={saving}
|
|
783
|
+
>
|
|
784
|
+
{saving
|
|
785
|
+
? props.texts.settingsSaveSavingButton
|
|
786
|
+
: props.texts.settingsSaveAgentsButton}
|
|
787
|
+
</button>
|
|
788
|
+
</form>
|
|
789
|
+
</section>
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
|
|
649
793
|
/* ────────────────────────────────────────────────────────────────────────── */
|
|
650
794
|
/* Section: Feature toggles */
|
|
651
795
|
/* ────────────────────────────────────────────────────────────────────────── */
|
package/src/types.ts
CHANGED
|
@@ -112,6 +112,17 @@ export interface AIHubDeployment {
|
|
|
112
112
|
* per-device choice.
|
|
113
113
|
*/
|
|
114
114
|
user_language_locked: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* ai-hub agent id auto-opened on load (skipping the agents picker). `null` =
|
|
117
|
+
* no default → the picker is shown. Ignored when only one agent exists (that
|
|
118
|
+
* one always opens). A dangling id falls back to the picker.
|
|
119
|
+
*/
|
|
120
|
+
default_agent_id: string | null;
|
|
121
|
+
/**
|
|
122
|
+
* When `false`, users can't switch agents — the "← Agents" back link is
|
|
123
|
+
* hidden. (Also hidden whenever only one agent exists, regardless of this.)
|
|
124
|
+
*/
|
|
125
|
+
agent_switcher_enabled: boolean;
|
|
115
126
|
/**
|
|
116
127
|
* Browser-facing URL of the self-hosted Letta ADE — only present when the
|
|
117
128
|
* caller's role is `power_user` or higher. Absent for `user`-role callers.
|