agent-relay-server 0.22.0 → 0.24.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/package.json +1 -1
- package/public/index.html +548 -387
- package/runner/src/adapter.ts +13 -0
- package/src/connectors.ts +37 -1
- package/src/lifecycle-manager.ts +1 -0
- package/src/managed-policy.ts +1 -0
- package/src/mcp.ts +9 -5
- package/src/memory-broker-base.ts +161 -0
- package/src/memory-command-broker.ts +10 -119
- package/src/memory-http-broker.ts +11 -141
- package/src/routes.ts +11 -8
package/public/index.html
CHANGED
|
@@ -10510,6 +10510,40 @@ function prepareForSpeech(markdown) {
|
|
|
10510
10510
|
}
|
|
10511
10511
|
//#endregion
|
|
10512
10512
|
//#region src/lib/voice.ts
|
|
10513
|
+
var KOKORO_VOICES = [
|
|
10514
|
+
{
|
|
10515
|
+
id: "am_michael",
|
|
10516
|
+
label: "Michael (US ♂)"
|
|
10517
|
+
},
|
|
10518
|
+
{
|
|
10519
|
+
id: "am_adam",
|
|
10520
|
+
label: "Adam (US ♂)"
|
|
10521
|
+
},
|
|
10522
|
+
{
|
|
10523
|
+
id: "af_heart",
|
|
10524
|
+
label: "Heart (US ♀)"
|
|
10525
|
+
},
|
|
10526
|
+
{
|
|
10527
|
+
id: "af_bella",
|
|
10528
|
+
label: "Bella (US ♀)"
|
|
10529
|
+
},
|
|
10530
|
+
{
|
|
10531
|
+
id: "af_nicole",
|
|
10532
|
+
label: "Nicole (US ♀)"
|
|
10533
|
+
},
|
|
10534
|
+
{
|
|
10535
|
+
id: "af_sarah",
|
|
10536
|
+
label: "Sarah (US ♀)"
|
|
10537
|
+
},
|
|
10538
|
+
{
|
|
10539
|
+
id: "bm_george",
|
|
10540
|
+
label: "George (UK ♂)"
|
|
10541
|
+
},
|
|
10542
|
+
{
|
|
10543
|
+
id: "bf_emma",
|
|
10544
|
+
label: "Emma (UK ♀)"
|
|
10545
|
+
}
|
|
10546
|
+
];
|
|
10513
10547
|
var MAX_CHUNK = 220;
|
|
10514
10548
|
/** Split into utterance-sized chunks (sentence boundaries; hard-split very long runs). */
|
|
10515
10549
|
function chunkForSpeech(text) {
|
|
@@ -10537,6 +10571,7 @@ var VoiceTts = class {
|
|
|
10537
10571
|
lang = "en-US";
|
|
10538
10572
|
mode = "kokoro";
|
|
10539
10573
|
kokoroVoice = "am_michael";
|
|
10574
|
+
browserVoice = "";
|
|
10540
10575
|
active = null;
|
|
10541
10576
|
queue = [];
|
|
10542
10577
|
currentChat = null;
|
|
@@ -10586,6 +10621,10 @@ var VoiceTts = class {
|
|
|
10586
10621
|
setKokoroVoice(voice) {
|
|
10587
10622
|
this.kokoroVoice = voice;
|
|
10588
10623
|
}
|
|
10624
|
+
/** Set the browser engine's voice by voiceURI. Empty = default for the language. */
|
|
10625
|
+
setBrowserVoice(uri) {
|
|
10626
|
+
this.browserVoice = uri;
|
|
10627
|
+
}
|
|
10589
10628
|
setActiveChat(chatId) {
|
|
10590
10629
|
if (chatId === this.active) return;
|
|
10591
10630
|
this.active = chatId;
|
|
@@ -10683,7 +10722,11 @@ var VoiceTts = class {
|
|
|
10683
10722
|
if (gen !== this.gen) return;
|
|
10684
10723
|
if (!synthAvailable || i >= chunks.length) return done();
|
|
10685
10724
|
const u = new SpeechSynthesisUtterance(chunks[i]);
|
|
10686
|
-
|
|
10725
|
+
const picked = this.browserVoice ? window.speechSynthesis.getVoices().find((v) => v.voiceURI === this.browserVoice) : void 0;
|
|
10726
|
+
if (picked) {
|
|
10727
|
+
u.voice = picked;
|
|
10728
|
+
u.lang = picked.lang;
|
|
10729
|
+
} else u.lang = this.lang || navigator.language || "en-US";
|
|
10687
10730
|
u.onend = () => this.speakBrowser(chunks, i + 1, gen, done);
|
|
10688
10731
|
u.onerror = () => this.speakBrowser(chunks, i + 1, gen, done);
|
|
10689
10732
|
window.speechSynthesis.speak(u);
|
|
@@ -10732,12 +10775,16 @@ var VoiceTts = class {
|
|
|
10732
10775
|
}
|
|
10733
10776
|
};
|
|
10734
10777
|
var voiceTts = new VoiceTts();
|
|
10735
|
-
/**
|
|
10736
|
-
* until the engine
|
|
10737
|
-
function
|
|
10778
|
+
/** The browser's individual speech voices ({uri, label, lang}), sorted by language
|
|
10779
|
+
* then name. May be empty until the engine loads (listen for `voiceschanged`). */
|
|
10780
|
+
function availableSpeechVoices() {
|
|
10738
10781
|
if (!synthAvailable) return [];
|
|
10739
10782
|
try {
|
|
10740
|
-
return
|
|
10783
|
+
return window.speechSynthesis.getVoices().map((v) => ({
|
|
10784
|
+
uri: v.voiceURI,
|
|
10785
|
+
label: `${v.name} (${v.lang})`,
|
|
10786
|
+
lang: v.lang
|
|
10787
|
+
})).sort((a, b) => a.lang.localeCompare(b.lang) || a.label.localeCompare(b.label));
|
|
10741
10788
|
} catch {
|
|
10742
10789
|
return [];
|
|
10743
10790
|
}
|
|
@@ -11344,6 +11391,14 @@ function agentPresence(now, agent, attention, pair) {
|
|
|
11344
11391
|
reconnecting: false,
|
|
11345
11392
|
badges
|
|
11346
11393
|
};
|
|
11394
|
+
if (lifecycleAction.startsWith("finalizing-")) return {
|
|
11395
|
+
label: `wrapping up (${lifecycleAction.slice(11)})`,
|
|
11396
|
+
tone: "warning",
|
|
11397
|
+
icon: "Hourglass",
|
|
11398
|
+
stale,
|
|
11399
|
+
reconnecting,
|
|
11400
|
+
badges
|
|
11401
|
+
};
|
|
11347
11402
|
if (lifecycleAction === "shutting-down") return {
|
|
11348
11403
|
label: "shutting down",
|
|
11349
11404
|
tone: "warning",
|
|
@@ -11717,6 +11772,7 @@ function toneToColor(tone) {
|
|
|
11717
11772
|
function statusDotColor(agent, now) {
|
|
11718
11773
|
if (!agent) return "bg-zinc-500 opacity-50";
|
|
11719
11774
|
if (agent.status === "offline") return "bg-zinc-500 opacity-50";
|
|
11775
|
+
if (typeof agent.meta?.lifecycleAction === "string" && agent.meta.lifecycleAction.startsWith("finalizing-")) return "bg-amber-500 animate-pulse";
|
|
11720
11776
|
if (agent.meta?.lifecycleAction === "shutting-down" || agent.meta?.lifecycleAction === "restarting") return "bg-yellow-500 animate-pulse";
|
|
11721
11777
|
if (agent.meta?.lifecycleAction === "killing") return "bg-red-500 animate-pulse";
|
|
11722
11778
|
if (providerBlockedState(agent)) return "bg-red-500 animate-pulse";
|
|
@@ -12176,6 +12232,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
|
|
|
12176
12232
|
voiceTtsLang: "en-US",
|
|
12177
12233
|
voiceTtsMode: "kokoro",
|
|
12178
12234
|
voiceTtsKokoroVoice: "am_michael",
|
|
12235
|
+
voiceTtsBrowserVoice: "",
|
|
12179
12236
|
voiceInputMode: "compose",
|
|
12180
12237
|
agentSort: "status",
|
|
12181
12238
|
agentSortDir: "asc",
|
|
@@ -12220,6 +12277,8 @@ var useRelayStore = create$1()(persist((set, get) => ({
|
|
|
12220
12277
|
chatAgentHostFilter: "",
|
|
12221
12278
|
chatAgentSort: "status",
|
|
12222
12279
|
chatAgentSortDir: "asc",
|
|
12280
|
+
chatAgentGroupBy: "",
|
|
12281
|
+
chatAgentFiltersCollapsed: true,
|
|
12223
12282
|
chatStatusEvents: {},
|
|
12224
12283
|
chatStickToBottom: true,
|
|
12225
12284
|
chatHasNewItems: false,
|
|
@@ -12382,6 +12441,10 @@ var useRelayStore = create$1()(persist((set, get) => ({
|
|
|
12382
12441
|
voiceTts.setKokoroVoice(voice);
|
|
12383
12442
|
set({ voiceTtsKokoroVoice: voice });
|
|
12384
12443
|
},
|
|
12444
|
+
setVoiceTtsBrowserVoice(uri) {
|
|
12445
|
+
voiceTts.setBrowserVoice(uri);
|
|
12446
|
+
set({ voiceTtsBrowserVoice: uri });
|
|
12447
|
+
},
|
|
12385
12448
|
setVoiceInputMode(mode) {
|
|
12386
12449
|
set({ voiceInputMode: mode });
|
|
12387
12450
|
},
|
|
@@ -12398,6 +12461,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
|
|
|
12398
12461
|
voiceTts.setLang(get().voiceTtsLang);
|
|
12399
12462
|
voiceTts.setMode(get().voiceTtsMode);
|
|
12400
12463
|
voiceTts.setKokoroVoice(get().voiceTtsKokoroVoice);
|
|
12464
|
+
voiceTts.setBrowserVoice(get().voiceTtsBrowserVoice);
|
|
12401
12465
|
syncVoiceActiveChat(get());
|
|
12402
12466
|
setUnauthorizedHandler(() => {
|
|
12403
12467
|
if (!get().authNeeded) set({
|
|
@@ -14520,6 +14584,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
|
|
|
14520
14584
|
voiceTtsLang: state.voiceTtsLang,
|
|
14521
14585
|
voiceTtsMode: state.voiceTtsMode,
|
|
14522
14586
|
voiceTtsKokoroVoice: state.voiceTtsKokoroVoice,
|
|
14587
|
+
voiceTtsBrowserVoice: state.voiceTtsBrowserVoice,
|
|
14523
14588
|
voiceInputMode: state.voiceInputMode,
|
|
14524
14589
|
agentSort: state.agentSort,
|
|
14525
14590
|
agentSortDir: state.agentSortDir,
|
|
@@ -14538,6 +14603,8 @@ var useRelayStore = create$1()(persist((set, get) => ({
|
|
|
14538
14603
|
chatAgentHostFilter: state.chatAgentHostFilter,
|
|
14539
14604
|
chatAgentSort: state.chatAgentSort,
|
|
14540
14605
|
chatAgentSortDir: state.chatAgentSortDir,
|
|
14606
|
+
chatAgentGroupBy: state.chatAgentGroupBy,
|
|
14607
|
+
chatAgentFiltersCollapsed: state.chatAgentFiltersCollapsed,
|
|
14541
14608
|
activityFilter: state.activityFilter,
|
|
14542
14609
|
analyticsPeriod: state.analyticsPeriod,
|
|
14543
14610
|
memoryFilters: state.memoryFilters,
|
|
@@ -77539,6 +77606,49 @@ function OverviewView() {
|
|
|
77539
77606
|
});
|
|
77540
77607
|
}
|
|
77541
77608
|
//#endregion
|
|
77609
|
+
//#region src/hooks/use-agent-terminal.ts
|
|
77610
|
+
/**
|
|
77611
|
+
* Open/close lifecycle for an agent's terminal session. Shared by the agent
|
|
77612
|
+
* card and the chat header — both POST a terminal-session, track the target,
|
|
77613
|
+
* and DELETE guest sessions on close. Pass null/undefined to no-op (e.g. when
|
|
77614
|
+
* no agent is selected).
|
|
77615
|
+
*/
|
|
77616
|
+
function useAgentTerminal(agentId) {
|
|
77617
|
+
const showError = useRelayStore((s) => s.showError);
|
|
77618
|
+
const [terminalOpen, setTerminalOpen] = (0, import_react.useState)(false);
|
|
77619
|
+
const [terminalTarget, setTerminalTarget] = (0, import_react.useState)(null);
|
|
77620
|
+
const [terminalOpening, setTerminalOpening] = (0, import_react.useState)(false);
|
|
77621
|
+
async function openTerminal() {
|
|
77622
|
+
if (!agentId || terminalOpening) return;
|
|
77623
|
+
setTerminalOpening(true);
|
|
77624
|
+
try {
|
|
77625
|
+
setTerminalTarget(await api("POST", "/agents/" + encodeURIComponent(agentId) + "/terminal-session"));
|
|
77626
|
+
setTerminalOpen(true);
|
|
77627
|
+
} catch (e) {
|
|
77628
|
+
showError("Terminal Failed", e.message);
|
|
77629
|
+
} finally {
|
|
77630
|
+
setTerminalOpening(false);
|
|
77631
|
+
}
|
|
77632
|
+
}
|
|
77633
|
+
function closeTerminal(open) {
|
|
77634
|
+
if (open) {
|
|
77635
|
+
setTerminalOpen(true);
|
|
77636
|
+
return;
|
|
77637
|
+
}
|
|
77638
|
+
setTerminalOpen(false);
|
|
77639
|
+
const target = terminalTarget;
|
|
77640
|
+
setTerminalTarget(null);
|
|
77641
|
+
if (agentId && target?.mode === "guest") api("DELETE", "/agents/" + encodeURIComponent(agentId) + "/terminal-session/" + encodeURIComponent(target.session)).catch(() => {});
|
|
77642
|
+
}
|
|
77643
|
+
return {
|
|
77644
|
+
terminalOpen,
|
|
77645
|
+
terminalTarget,
|
|
77646
|
+
terminalOpening,
|
|
77647
|
+
openTerminal,
|
|
77648
|
+
closeTerminal
|
|
77649
|
+
};
|
|
77650
|
+
}
|
|
77651
|
+
//#endregion
|
|
77542
77652
|
//#region node_modules/comma-separated-tokens/index.js
|
|
77543
77653
|
/**
|
|
77544
77654
|
* Serialize an array of strings or numbers to comma-separated tokens.
|
|
@@ -108818,7 +108928,7 @@ function ContextMeter({ agent }) {
|
|
|
108818
108928
|
function runtimeChips(agent) {
|
|
108819
108929
|
const spawned = isRelaySpawned(agent);
|
|
108820
108930
|
const approval = approvalMode(agent);
|
|
108821
|
-
const workspace =
|
|
108931
|
+
const workspace = agentWorkspaceMeta(agent);
|
|
108822
108932
|
return [
|
|
108823
108933
|
{
|
|
108824
108934
|
key: "origin",
|
|
@@ -108858,7 +108968,7 @@ function approvalMode(agent) {
|
|
|
108858
108968
|
className: "border-amber-500/30 bg-amber-500/10 text-amber-300"
|
|
108859
108969
|
};
|
|
108860
108970
|
}
|
|
108861
|
-
function
|
|
108971
|
+
function agentWorkspaceMeta(agent) {
|
|
108862
108972
|
const workspace = recordValue(agent.meta?.workspace);
|
|
108863
108973
|
const requested = stringValue(workspace.requestedMode) || stringValue(agent.meta?.workspaceMode);
|
|
108864
108974
|
const effective = stringValue(workspace.mode);
|
|
@@ -108868,6 +108978,7 @@ function workspaceMode(agent) {
|
|
|
108868
108978
|
label: "Isolated",
|
|
108869
108979
|
title: "Workspace: isolated worktree",
|
|
108870
108980
|
Icon: GitBranch,
|
|
108981
|
+
iconColor: "text-violet-300",
|
|
108871
108982
|
className: "border-violet-500/30 bg-violet-500/10 text-violet-300"
|
|
108872
108983
|
};
|
|
108873
108984
|
if (mode === "shared") return {
|
|
@@ -108875,6 +108986,7 @@ function workspaceMode(agent) {
|
|
|
108875
108986
|
label: "Shared",
|
|
108876
108987
|
title: "Workspace: shared repo checkout",
|
|
108877
108988
|
Icon: Share2,
|
|
108989
|
+
iconColor: "text-cyan-300",
|
|
108878
108990
|
className: "border-cyan-500/30 bg-cyan-500/10 text-cyan-300"
|
|
108879
108991
|
};
|
|
108880
108992
|
return {
|
|
@@ -108882,12 +108994,18 @@ function workspaceMode(agent) {
|
|
|
108882
108994
|
label: "Default",
|
|
108883
108995
|
title: effective ? `Workspace: default/inherit, resolved to ${effective}` : "Workspace: default/inherit",
|
|
108884
108996
|
Icon: Layers,
|
|
108997
|
+
iconColor: "text-zinc-400",
|
|
108885
108998
|
className: "border-zinc-500/30 bg-zinc-500/10 text-zinc-300"
|
|
108886
108999
|
};
|
|
108887
109000
|
}
|
|
108888
109001
|
function recordValue(value) {
|
|
108889
109002
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
108890
109003
|
}
|
|
109004
|
+
function agentProjectName(agent) {
|
|
109005
|
+
const workspace = recordValue(agent.meta?.workspace);
|
|
109006
|
+
const root = stringValue(workspace.repoRoot) || stringValue(workspace.sourceCwd) || stringValue(agent.meta?.cwd);
|
|
109007
|
+
return root ? shortPath$1(root, 2) : "";
|
|
109008
|
+
}
|
|
108891
109009
|
function runtimeBadges(agent) {
|
|
108892
109010
|
const caps = agent.providerCapabilities;
|
|
108893
109011
|
const context = agent.context;
|
|
@@ -108903,40 +109021,47 @@ function runtimeBadges(agent) {
|
|
|
108903
109021
|
if (context) badges.push(`${Math.round(context.utilization * 100)}% ctx`);
|
|
108904
109022
|
return badges;
|
|
108905
109023
|
}
|
|
108906
|
-
function ContextRing({ utilization, className }) {
|
|
109024
|
+
function ContextRing({ utilization, size = 26, className }) {
|
|
108907
109025
|
const pct = Math.round(Math.min(1, Math.max(0, utilization)) * 100);
|
|
108908
|
-
const
|
|
109026
|
+
const stroke = size >= 24 ? 3 : 2.5;
|
|
109027
|
+
const c = size / 2;
|
|
109028
|
+
const r = c - stroke;
|
|
108909
109029
|
const circumference = 2 * Math.PI * r;
|
|
108910
109030
|
const offset = circumference * (1 - Math.min(1, Math.max(0, utilization)));
|
|
108911
109031
|
const color = utilization > .8 ? "text-red-400" : utilization > .5 ? "text-yellow-400" : "text-emerald-400";
|
|
108912
109032
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
108913
109033
|
className: `relative shrink-0 ${className ?? ""}`,
|
|
109034
|
+
style: {
|
|
109035
|
+
width: size,
|
|
109036
|
+
height: size
|
|
109037
|
+
},
|
|
108914
109038
|
title: `Context: ${pct}%`,
|
|
108915
109039
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
|
|
108916
|
-
width:
|
|
108917
|
-
height:
|
|
108918
|
-
viewBox:
|
|
109040
|
+
width: size,
|
|
109041
|
+
height: size,
|
|
109042
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
108919
109043
|
className: "rotate-[-90deg]",
|
|
108920
109044
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
|
|
108921
|
-
cx:
|
|
108922
|
-
cy:
|
|
109045
|
+
cx: c,
|
|
109046
|
+
cy: c,
|
|
108923
109047
|
r,
|
|
108924
109048
|
fill: "none",
|
|
108925
|
-
strokeWidth:
|
|
109049
|
+
strokeWidth: stroke,
|
|
108926
109050
|
className: "stroke-muted-foreground/20"
|
|
108927
109051
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
|
|
108928
|
-
cx:
|
|
108929
|
-
cy:
|
|
109052
|
+
cx: c,
|
|
109053
|
+
cy: c,
|
|
108930
109054
|
r,
|
|
108931
109055
|
fill: "none",
|
|
108932
|
-
strokeWidth:
|
|
109056
|
+
strokeWidth: stroke,
|
|
108933
109057
|
strokeDasharray: circumference,
|
|
108934
109058
|
strokeDashoffset: offset,
|
|
108935
109059
|
strokeLinecap: "round",
|
|
108936
109060
|
className: `${color} stroke-current transition-all duration-500`
|
|
108937
109061
|
})]
|
|
108938
109062
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
108939
|
-
className: `absolute inset-0 flex items-center justify-center
|
|
109063
|
+
className: `absolute inset-0 flex items-center justify-center font-semibold leading-none ${color}`,
|
|
109064
|
+
style: { fontSize: size >= 24 ? 8 : 7 },
|
|
108940
109065
|
children: pct
|
|
108941
109066
|
})]
|
|
108942
109067
|
});
|
|
@@ -125643,6 +125768,8 @@ function AgentListPanel({ threads, onSelectAgent }) {
|
|
|
125643
125768
|
const chatAgentTagFilter = useRelayStore((s) => s.chatAgentTagFilter);
|
|
125644
125769
|
const chatAgentCapFilter = useRelayStore((s) => s.chatAgentCapFilter);
|
|
125645
125770
|
const chatAgentHostFilter = useRelayStore((s) => s.chatAgentHostFilter);
|
|
125771
|
+
const chatAgentGroupBy = useRelayStore((s) => s.chatAgentGroupBy);
|
|
125772
|
+
const chatAgentFiltersCollapsed = useRelayStore((s) => s.chatAgentFiltersCollapsed);
|
|
125646
125773
|
const now = useNow();
|
|
125647
125774
|
const chatAgents = useChatAgents();
|
|
125648
125775
|
const threadByPeer = (0, import_react.useMemo)(() => {
|
|
@@ -125666,161 +125793,287 @@ function AgentListPanel({ threads, onSelectAgent }) {
|
|
|
125666
125793
|
const uniqueTags = (0, import_react.useMemo)(() => [...new Set(chatAgents.flatMap((a) => a.tags || []))], [chatAgents]);
|
|
125667
125794
|
const uniqueCaps = (0, import_react.useMemo)(() => [...new Set(chatAgents.flatMap((a) => userFacingCapabilities(a.capabilities || [])))], [chatAgents]);
|
|
125668
125795
|
const uniqueHosts = useUniqueHosts();
|
|
125796
|
+
const groupedAgents = (0, import_react.useMemo)(() => {
|
|
125797
|
+
if (chatAgentGroupBy !== "project") return null;
|
|
125798
|
+
const groups = /* @__PURE__ */ new Map();
|
|
125799
|
+
for (const a of sortedAgents) {
|
|
125800
|
+
const key = agentProjectName(a) || "No project";
|
|
125801
|
+
const bucket = groups.get(key);
|
|
125802
|
+
if (bucket) bucket.push(a);
|
|
125803
|
+
else groups.set(key, [a]);
|
|
125804
|
+
}
|
|
125805
|
+
return [...groups.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
125806
|
+
}, [sortedAgents, chatAgentGroupBy]);
|
|
125807
|
+
const activeFilterCount = [
|
|
125808
|
+
chatAgentProviderFilter,
|
|
125809
|
+
chatAgentStatusFilter,
|
|
125810
|
+
chatAgentTagFilter,
|
|
125811
|
+
chatAgentCapFilter,
|
|
125812
|
+
chatAgentHostFilter
|
|
125813
|
+
].filter(Boolean).length;
|
|
125669
125814
|
function handleSelect(id) {
|
|
125670
125815
|
openInboxThread(id, threadByPeer.get(id)?.messages);
|
|
125671
125816
|
onSelectAgent?.(id);
|
|
125672
125817
|
}
|
|
125673
|
-
|
|
125674
|
-
|
|
125675
|
-
|
|
125676
|
-
|
|
125677
|
-
|
|
125678
|
-
|
|
125679
|
-
|
|
125680
|
-
|
|
125681
|
-
|
|
125682
|
-
|
|
125683
|
-
|
|
125684
|
-
|
|
125685
|
-
|
|
125686
|
-
|
|
125687
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
125688
|
-
|
|
125689
|
-
|
|
125690
|
-
|
|
125691
|
-
|
|
125692
|
-
|
|
125818
|
+
function renderAgentRow(agent) {
|
|
125819
|
+
const thread = threadByPeer.get(agent.id);
|
|
125820
|
+
const unread = thread?.attention.unread || 0;
|
|
125821
|
+
const lastMsg = thread?.previewMessage;
|
|
125822
|
+
const lastActivityAt = threadActivityTimestamp(thread);
|
|
125823
|
+
const isSelected = selectedInboxThread === agent.id;
|
|
125824
|
+
const ws = agentWorkspaceMeta(agent);
|
|
125825
|
+
const project = agentProjectName(agent);
|
|
125826
|
+
const WsIcon = ws.Icon;
|
|
125827
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
125828
|
+
className: cn$2("w-full text-left px-3 py-2.5 flex items-start gap-2.5 hover:bg-muted/50 transition-colors border-b border-border/50", isSelected && "bg-muted/60"),
|
|
125829
|
+
onClick: () => handleSelect(agent.id),
|
|
125830
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125831
|
+
className: "relative shrink-0 mt-0.5",
|
|
125832
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentTypeIcon, { agent }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusDot, {
|
|
125833
|
+
agent,
|
|
125834
|
+
now,
|
|
125835
|
+
className: "absolute -bottom-0.5 -right-0.5 w-2 h-2"
|
|
125836
|
+
})]
|
|
125837
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125838
|
+
className: "flex-1 min-w-0",
|
|
125839
|
+
children: [
|
|
125840
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125841
|
+
className: "flex items-center justify-between gap-1 mb-0.5",
|
|
125842
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125843
|
+
className: "text-xs font-medium truncate",
|
|
125844
|
+
children: displayName(agent)
|
|
125845
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125846
|
+
className: "flex items-center gap-1 shrink-0",
|
|
125847
|
+
children: [lastActivityAt > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125848
|
+
className: "text-[10px] text-muted-foreground",
|
|
125849
|
+
title: fmtTime$1(lastActivityAt),
|
|
125850
|
+
children: timeAgo(now, lastActivityAt)
|
|
125851
|
+
}), unread > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
|
|
125852
|
+
className: "bg-red-500 text-white text-xs min-w-[18px] h-[18px] flex items-center justify-center shrink-0 px-1",
|
|
125853
|
+
children: unread
|
|
125854
|
+
})]
|
|
125855
|
+
})]
|
|
125856
|
+
}),
|
|
125857
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125858
|
+
className: "flex items-center gap-1.5 mb-0.5 text-[10px] text-muted-foreground",
|
|
125693
125859
|
children: [
|
|
125694
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
125695
|
-
|
|
125696
|
-
|
|
125697
|
-
|
|
125698
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125699
|
-
value: "name",
|
|
125700
|
-
children: "By name"
|
|
125860
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125861
|
+
title: ws.title,
|
|
125862
|
+
className: "inline-flex shrink-0",
|
|
125863
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WsIcon, { className: cn$2("h-3 w-3", ws.iconColor) })
|
|
125701
125864
|
}),
|
|
125702
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
125703
|
-
|
|
125704
|
-
children:
|
|
125865
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125866
|
+
className: "truncate",
|
|
125867
|
+
children: project || ws.label
|
|
125705
125868
|
}),
|
|
125706
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
125707
|
-
|
|
125708
|
-
children:
|
|
125869
|
+
agent.context && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125870
|
+
className: "ml-auto shrink-0",
|
|
125871
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextRing, {
|
|
125872
|
+
utilization: agent.context.utilization,
|
|
125873
|
+
size: 18
|
|
125874
|
+
})
|
|
125709
125875
|
})
|
|
125710
125876
|
]
|
|
125711
|
-
}),
|
|
125712
|
-
|
|
125713
|
-
|
|
125714
|
-
|
|
125715
|
-
|
|
125716
|
-
|
|
125717
|
-
|
|
125718
|
-
|
|
125719
|
-
|
|
125720
|
-
|
|
125721
|
-
|
|
125722
|
-
|
|
125723
|
-
|
|
125724
|
-
|
|
125877
|
+
}),
|
|
125878
|
+
lastMsg && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
125879
|
+
className: "text-xs text-muted-foreground truncate leading-tight",
|
|
125880
|
+
children: [lastMsg.from === "user" ? "You: " : "", messagePreview(lastMsg)]
|
|
125881
|
+
}),
|
|
125882
|
+
agent.status !== "offline" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125883
|
+
className: "mt-1",
|
|
125884
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125885
|
+
className: cn$2("text-xs", agent.status === "busy" ? "text-yellow-400" : "text-emerald-400"),
|
|
125886
|
+
children: agent.status
|
|
125887
|
+
})
|
|
125888
|
+
})
|
|
125889
|
+
]
|
|
125890
|
+
})]
|
|
125891
|
+
}) }, agent.id);
|
|
125892
|
+
}
|
|
125893
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125894
|
+
className: "flex flex-col h-full border-r border-border min-w-0",
|
|
125895
|
+
children: [
|
|
125896
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125897
|
+
className: "p-3 border-b border-border space-y-2",
|
|
125898
|
+
children: [
|
|
125899
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125900
|
+
className: "relative",
|
|
125901
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground pointer-events-none" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
|
|
125902
|
+
placeholder: "Search agents...",
|
|
125903
|
+
value: chatAgentSearch,
|
|
125904
|
+
onChange: (e) => set({ chatAgentSearch: e.target.value }),
|
|
125905
|
+
className: "pl-8 h-7 text-xs"
|
|
125906
|
+
})]
|
|
125907
|
+
}),
|
|
125908
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
125909
|
+
type: "button",
|
|
125910
|
+
onClick: () => set({ chatAgentFiltersCollapsed: !chatAgentFiltersCollapsed }),
|
|
125911
|
+
className: "sm:hidden flex w-full items-center justify-between h-6 px-1.5 text-xs rounded-md border border-border bg-background text-muted-foreground",
|
|
125912
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
125913
|
+
className: "inline-flex items-center gap-1.5",
|
|
125725
125914
|
children: [
|
|
125726
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
125727
|
-
|
|
125728
|
-
|
|
125729
|
-
|
|
125730
|
-
|
|
125731
|
-
value: "claude",
|
|
125732
|
-
children: "Claude"
|
|
125733
|
-
}),
|
|
125734
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125735
|
-
value: "codex",
|
|
125736
|
-
children: "Codex"
|
|
125737
|
-
}),
|
|
125738
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125739
|
-
value: "agent",
|
|
125740
|
-
children: "Agent"
|
|
125741
|
-
}),
|
|
125742
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125743
|
-
value: "user",
|
|
125744
|
-
children: "User"
|
|
125915
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SlidersHorizontal, { className: "w-3.5 h-3.5" }),
|
|
125916
|
+
"Filters & sort",
|
|
125917
|
+
activeFilterCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
|
|
125918
|
+
className: "bg-primary/20 text-primary text-[10px] h-4 min-w-4 px-1 flex items-center justify-center",
|
|
125919
|
+
children: activeFilterCount
|
|
125745
125920
|
})
|
|
125746
125921
|
]
|
|
125747
|
-
}),
|
|
125748
|
-
|
|
125749
|
-
|
|
125750
|
-
|
|
125751
|
-
|
|
125752
|
-
|
|
125753
|
-
|
|
125922
|
+
}), chatAgentFiltersCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, { className: "w-3.5 h-3.5" })]
|
|
125923
|
+
}),
|
|
125924
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125925
|
+
className: cn$2("space-y-2", chatAgentFiltersCollapsed ? "hidden" : "block", "sm:block"),
|
|
125926
|
+
children: [
|
|
125927
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125928
|
+
className: "flex items-center gap-1.5",
|
|
125929
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
125930
|
+
value: chatAgentSort,
|
|
125931
|
+
onChange: (e) => set({ chatAgentSort: e.target.value }),
|
|
125932
|
+
className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
|
|
125933
|
+
children: [
|
|
125934
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125935
|
+
value: "status",
|
|
125936
|
+
children: "By status"
|
|
125937
|
+
}),
|
|
125938
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125939
|
+
value: "name",
|
|
125940
|
+
children: "By name"
|
|
125941
|
+
}),
|
|
125942
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125943
|
+
value: "lastSeen",
|
|
125944
|
+
children: "By last seen"
|
|
125945
|
+
}),
|
|
125946
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125947
|
+
value: "lastMessage",
|
|
125948
|
+
children: "By last activity"
|
|
125949
|
+
})
|
|
125950
|
+
]
|
|
125951
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
125952
|
+
variant: "ghost",
|
|
125953
|
+
size: "icon-xs",
|
|
125954
|
+
onClick: () => set({ chatAgentSortDir: chatAgentSortDir === "asc" ? "desc" : "asc" }),
|
|
125955
|
+
children: chatAgentSortDir === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUpNarrowWide, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowDownWideNarrow, { className: "w-3.5 h-3.5" })
|
|
125956
|
+
})]
|
|
125957
|
+
}),
|
|
125958
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
125959
|
+
value: chatAgentGroupBy,
|
|
125960
|
+
onChange: (e) => set({ chatAgentGroupBy: e.target.value }),
|
|
125961
|
+
className: "w-full h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
|
|
125962
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125754
125963
|
value: "",
|
|
125755
|
-
children: "
|
|
125756
|
-
}),
|
|
125757
|
-
|
|
125758
|
-
|
|
125759
|
-
|
|
125760
|
-
|
|
125761
|
-
|
|
125762
|
-
|
|
125763
|
-
|
|
125764
|
-
|
|
125765
|
-
|
|
125766
|
-
|
|
125767
|
-
|
|
125768
|
-
|
|
125769
|
-
|
|
125770
|
-
|
|
125771
|
-
|
|
125772
|
-
|
|
125773
|
-
|
|
125774
|
-
|
|
125775
|
-
|
|
125776
|
-
|
|
125777
|
-
|
|
125778
|
-
|
|
125779
|
-
|
|
125780
|
-
|
|
125781
|
-
|
|
125782
|
-
|
|
125783
|
-
|
|
125784
|
-
|
|
125785
|
-
|
|
125786
|
-
|
|
125787
|
-
|
|
125788
|
-
|
|
125789
|
-
|
|
125790
|
-
|
|
125791
|
-
|
|
125792
|
-
|
|
125793
|
-
|
|
125794
|
-
|
|
125795
|
-
|
|
125796
|
-
|
|
125797
|
-
|
|
125798
|
-
|
|
125799
|
-
|
|
125800
|
-
|
|
125801
|
-
|
|
125802
|
-
|
|
125803
|
-
|
|
125804
|
-
|
|
125805
|
-
|
|
125806
|
-
|
|
125807
|
-
|
|
125808
|
-
|
|
125809
|
-
|
|
125810
|
-
|
|
125811
|
-
|
|
125812
|
-
|
|
125813
|
-
|
|
125814
|
-
|
|
125815
|
-
|
|
125816
|
-
|
|
125817
|
-
|
|
125818
|
-
|
|
125964
|
+
children: "No grouping"
|
|
125965
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125966
|
+
value: "project",
|
|
125967
|
+
children: "Group by project"
|
|
125968
|
+
})]
|
|
125969
|
+
}),
|
|
125970
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125971
|
+
className: "flex gap-1",
|
|
125972
|
+
children: [
|
|
125973
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
125974
|
+
value: chatAgentProviderFilter,
|
|
125975
|
+
onChange: (e) => set({ chatAgentProviderFilter: e.target.value }),
|
|
125976
|
+
className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
|
|
125977
|
+
children: [
|
|
125978
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125979
|
+
value: "",
|
|
125980
|
+
children: "All types"
|
|
125981
|
+
}),
|
|
125982
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125983
|
+
value: "claude",
|
|
125984
|
+
children: "Claude"
|
|
125985
|
+
}),
|
|
125986
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125987
|
+
value: "codex",
|
|
125988
|
+
children: "Codex"
|
|
125989
|
+
}),
|
|
125990
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125991
|
+
value: "agent",
|
|
125992
|
+
children: "Agent"
|
|
125993
|
+
}),
|
|
125994
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
125995
|
+
value: "user",
|
|
125996
|
+
children: "User"
|
|
125997
|
+
})
|
|
125998
|
+
]
|
|
125999
|
+
}),
|
|
126000
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
126001
|
+
value: chatAgentStatusFilter,
|
|
126002
|
+
onChange: (e) => set({ chatAgentStatusFilter: e.target.value }),
|
|
126003
|
+
className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
|
|
126004
|
+
children: [
|
|
126005
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126006
|
+
value: "",
|
|
126007
|
+
children: "All status"
|
|
126008
|
+
}),
|
|
126009
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126010
|
+
value: "online",
|
|
126011
|
+
children: "Online"
|
|
126012
|
+
}),
|
|
126013
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126014
|
+
value: "idle",
|
|
126015
|
+
children: "Idle"
|
|
126016
|
+
}),
|
|
126017
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126018
|
+
value: "busy",
|
|
126019
|
+
children: "Busy"
|
|
126020
|
+
}),
|
|
126021
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126022
|
+
value: "offline",
|
|
126023
|
+
children: "Offline"
|
|
126024
|
+
})
|
|
126025
|
+
]
|
|
126026
|
+
}),
|
|
126027
|
+
uniqueHosts.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
126028
|
+
value: chatAgentHostFilter,
|
|
126029
|
+
onChange: (e) => set({ chatAgentHostFilter: e.target.value }),
|
|
126030
|
+
className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
|
|
126031
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126032
|
+
value: "",
|
|
126033
|
+
children: "All hosts"
|
|
126034
|
+
}), uniqueHosts.map((h) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126035
|
+
value: h,
|
|
126036
|
+
children: h
|
|
126037
|
+
}, h))]
|
|
126038
|
+
})
|
|
126039
|
+
]
|
|
126040
|
+
}),
|
|
126041
|
+
(uniqueTags.length > 0 || uniqueCaps.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
126042
|
+
className: "flex gap-1",
|
|
126043
|
+
children: [uniqueTags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
126044
|
+
value: chatAgentTagFilter,
|
|
126045
|
+
onChange: (e) => set({ chatAgentTagFilter: e.target.value }),
|
|
126046
|
+
className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
|
|
126047
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126048
|
+
value: "",
|
|
126049
|
+
children: "All tags"
|
|
126050
|
+
}), uniqueTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", {
|
|
126051
|
+
value: t,
|
|
126052
|
+
children: ["#", t]
|
|
126053
|
+
}, t))]
|
|
126054
|
+
}), uniqueCaps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
126055
|
+
value: chatAgentCapFilter,
|
|
126056
|
+
onChange: (e) => set({ chatAgentCapFilter: e.target.value }),
|
|
126057
|
+
className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
|
|
126058
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126059
|
+
value: "",
|
|
126060
|
+
children: "All caps"
|
|
126061
|
+
}), uniqueCaps.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
126062
|
+
value: c,
|
|
126063
|
+
children: c
|
|
126064
|
+
}, c))]
|
|
126065
|
+
})]
|
|
126066
|
+
})
|
|
126067
|
+
]
|
|
126068
|
+
})
|
|
126069
|
+
]
|
|
126070
|
+
}),
|
|
126071
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
125819
126072
|
type: "button",
|
|
125820
|
-
className: "flex w-full items-
|
|
126073
|
+
className: "flex w-full shrink-0 items-center gap-2.5 border-b border-border px-3 py-2 text-left transition-colors hover:bg-muted/50",
|
|
125821
126074
|
onClick: openAgentSpawn,
|
|
125822
126075
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125823
|
-
className: "
|
|
126076
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-dashed border-primary/50 bg-primary/10 text-primary",
|
|
125824
126077
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, { className: "h-3.5 w-3.5" })
|
|
125825
126078
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125826
126079
|
className: "min-w-0 flex-1",
|
|
@@ -125828,71 +126081,31 @@ function AgentListPanel({ threads, onSelectAgent }) {
|
|
|
125828
126081
|
className: "text-xs font-medium",
|
|
125829
126082
|
children: "New Agent"
|
|
125830
126083
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125831
|
-
className: "
|
|
126084
|
+
className: "truncate text-[10px] text-muted-foreground",
|
|
125832
126085
|
children: "Spawn a fresh chat agent"
|
|
125833
126086
|
})]
|
|
125834
126087
|
})]
|
|
125835
|
-
}),
|
|
125836
|
-
|
|
125837
|
-
|
|
125838
|
-
|
|
125839
|
-
|
|
125840
|
-
|
|
125841
|
-
|
|
125842
|
-
|
|
125843
|
-
const unread = thread?.attention.unread || 0;
|
|
125844
|
-
const lastMsg = thread?.previewMessage;
|
|
125845
|
-
const lastActivityAt = threadActivityTimestamp(thread);
|
|
125846
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
125847
|
-
className: cn$2("w-full text-left px-3 py-2.5 flex items-start gap-2.5 hover:bg-muted/50 transition-colors border-b border-border/50", selectedInboxThread === agent.id && "bg-muted/60"),
|
|
125848
|
-
onClick: () => handleSelect(agent.id),
|
|
125849
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125850
|
-
className: "relative shrink-0 mt-0.5",
|
|
125851
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentTypeIcon, { agent }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusDot, {
|
|
125852
|
-
agent,
|
|
125853
|
-
now,
|
|
125854
|
-
className: "absolute -bottom-0.5 -right-0.5 w-2 h-2"
|
|
125855
|
-
})]
|
|
125856
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125857
|
-
className: "flex-1 min-w-0",
|
|
125858
|
-
children: [
|
|
125859
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125860
|
-
className: "flex items-center justify-between gap-1 mb-0.5",
|
|
125861
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125862
|
-
className: "text-xs font-medium truncate",
|
|
125863
|
-
children: displayName(agent)
|
|
125864
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125865
|
-
className: "flex items-center gap-1 shrink-0",
|
|
125866
|
-
children: [lastActivityAt > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125867
|
-
className: "text-[10px] text-muted-foreground",
|
|
125868
|
-
title: fmtTime$1(lastActivityAt),
|
|
125869
|
-
children: timeAgo(now, lastActivityAt)
|
|
125870
|
-
}), unread > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
|
|
125871
|
-
className: "bg-red-500 text-white text-xs min-w-[18px] h-[18px] flex items-center justify-center shrink-0 px-1",
|
|
125872
|
-
children: unread
|
|
125873
|
-
})]
|
|
125874
|
-
})]
|
|
125875
|
-
}),
|
|
125876
|
-
typeof agent.meta?.cwd === "string" && agent.meta.cwd && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125877
|
-
className: "font-mono text-[10px] text-muted-foreground/60 truncate",
|
|
125878
|
-
children: shortPath$1(agent.meta.cwd)
|
|
125879
|
-
}),
|
|
125880
|
-
lastMsg && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
125881
|
-
className: "text-xs text-muted-foreground truncate leading-tight",
|
|
125882
|
-
children: [lastMsg.from === "user" ? "You: " : "", messagePreview(lastMsg)]
|
|
125883
|
-
}),
|
|
125884
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125885
|
-
className: "flex flex-wrap gap-1 mt-1",
|
|
125886
|
-
children: agent.status !== "offline" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125887
|
-
className: cn$2("text-xs", agent.status === "busy" ? "text-yellow-400" : agent.status === "idle" ? "text-emerald-400" : "text-emerald-400"),
|
|
125888
|
-
children: agent.status
|
|
125889
|
-
})
|
|
125890
|
-
})
|
|
125891
|
-
]
|
|
126088
|
+
}),
|
|
126089
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
126090
|
+
className: "flex-1 overflow-y-auto",
|
|
126091
|
+
children: sortedAgents.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
126092
|
+
className: "flex flex-col items-center justify-center py-10 text-center px-4",
|
|
126093
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bot, { className: "w-8 h-8 text-zinc-600 mb-2" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
126094
|
+
className: "text-xs text-muted-foreground",
|
|
126095
|
+
children: "No agents match filters"
|
|
125892
126096
|
})]
|
|
125893
|
-
})
|
|
125894
|
-
|
|
125895
|
-
|
|
126097
|
+
}) : groupedAgents ? groupedAgents.map(([project, agents]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
126098
|
+
className: "sticky top-0 z-10 flex items-center gap-1.5 border-b border-border/50 bg-background/95 px-3 py-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground backdrop-blur",
|
|
126099
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
126100
|
+
className: "truncate",
|
|
126101
|
+
children: project
|
|
126102
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
126103
|
+
className: "text-muted-foreground/50",
|
|
126104
|
+
children: agents.length
|
|
126105
|
+
})]
|
|
126106
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: agents.map(renderAgentRow) })] }, project)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: sortedAgents.map(renderAgentRow) })
|
|
126107
|
+
})
|
|
126108
|
+
]
|
|
125896
126109
|
});
|
|
125897
126110
|
}
|
|
125898
126111
|
function chatTimestamp(iso) {
|
|
@@ -125952,40 +126165,6 @@ var TIMELINE_STATUS_LABELS = {
|
|
|
125952
126165
|
var TIMELINE_STATUSES = new Set(Object.keys(TIMELINE_STATUS_LABELS));
|
|
125953
126166
|
var STATUS_DEDUPE_WINDOW_MS = 3e3;
|
|
125954
126167
|
var CHAT_BOTTOM_THRESHOLD_PX = 96;
|
|
125955
|
-
var KOKORO_VOICES = [
|
|
125956
|
-
{
|
|
125957
|
-
id: "am_michael",
|
|
125958
|
-
label: "Michael (US ♂)"
|
|
125959
|
-
},
|
|
125960
|
-
{
|
|
125961
|
-
id: "am_adam",
|
|
125962
|
-
label: "Adam (US ♂)"
|
|
125963
|
-
},
|
|
125964
|
-
{
|
|
125965
|
-
id: "af_heart",
|
|
125966
|
-
label: "Heart (US ♀)"
|
|
125967
|
-
},
|
|
125968
|
-
{
|
|
125969
|
-
id: "af_bella",
|
|
125970
|
-
label: "Bella (US ♀)"
|
|
125971
|
-
},
|
|
125972
|
-
{
|
|
125973
|
-
id: "af_nicole",
|
|
125974
|
-
label: "Nicole (US ♀)"
|
|
125975
|
-
},
|
|
125976
|
-
{
|
|
125977
|
-
id: "af_sarah",
|
|
125978
|
-
label: "Sarah (US ♀)"
|
|
125979
|
-
},
|
|
125980
|
-
{
|
|
125981
|
-
id: "bm_george",
|
|
125982
|
-
label: "George (UK ♂)"
|
|
125983
|
-
},
|
|
125984
|
-
{
|
|
125985
|
-
id: "bf_emma",
|
|
125986
|
-
label: "Emma (UK ♀)"
|
|
125987
|
-
}
|
|
125988
|
-
];
|
|
125989
126168
|
function StatusMarker({ event }) {
|
|
125990
126169
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125991
126170
|
className: "flex items-center justify-center gap-2 py-2 my-1",
|
|
@@ -127145,30 +127324,13 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127145
127324
|
const fetchOrchestrators = useRelayStore((s) => s.fetchOrchestrators);
|
|
127146
127325
|
const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled);
|
|
127147
127326
|
const setVoiceTtsEnabled = useRelayStore((s) => s.setVoiceTtsEnabled);
|
|
127148
|
-
const voiceTtsLang = useRelayStore((s) => s.voiceTtsLang);
|
|
127149
|
-
const setVoiceTtsLang = useRelayStore((s) => s.setVoiceTtsLang);
|
|
127150
|
-
const voiceTtsMode = useRelayStore((s) => s.voiceTtsMode);
|
|
127151
|
-
const setVoiceTtsMode = useRelayStore((s) => s.setVoiceTtsMode);
|
|
127152
|
-
const voiceTtsKokoroVoice = useRelayStore((s) => s.voiceTtsKokoroVoice);
|
|
127153
|
-
const setVoiceTtsKokoroVoice = useRelayStore((s) => s.setVoiceTtsKokoroVoice);
|
|
127154
127327
|
const voiceInputMode = useRelayStore((s) => s.voiceInputMode);
|
|
127155
|
-
const [speechLangs, setSpeechLangs] = (0, import_react.useState)(() => availableSpeechLangs());
|
|
127156
|
-
(0, import_react.useEffect)(() => {
|
|
127157
|
-
if (!voiceTts.available) return;
|
|
127158
|
-
const refresh = () => setSpeechLangs(availableSpeechLangs());
|
|
127159
|
-
refresh();
|
|
127160
|
-
window.speechSynthesis.addEventListener?.("voiceschanged", refresh);
|
|
127161
|
-
return () => window.speechSynthesis.removeEventListener?.("voiceschanged", refresh);
|
|
127162
|
-
}, []);
|
|
127163
127328
|
const fileInputRef = (0, import_react.useRef)(null);
|
|
127164
127329
|
const pttRecorderRef = (0, import_react.useRef)(null);
|
|
127165
127330
|
const [micState, setMicState] = (0, import_react.useState)("idle");
|
|
127166
127331
|
const pendingAttachmentsRef = (0, import_react.useRef)([]);
|
|
127167
127332
|
const [pendingAttachments, setPendingAttachments] = (0, import_react.useState)([]);
|
|
127168
127333
|
const [dragActive, setDragActive] = (0, import_react.useState)(false);
|
|
127169
|
-
const [terminalOpen, setTerminalOpen] = (0, import_react.useState)(false);
|
|
127170
|
-
const [terminalTarget, setTerminalTarget] = (0, import_react.useState)(null);
|
|
127171
|
-
const [terminalOpening, setTerminalOpening] = (0, import_react.useState)(false);
|
|
127172
127334
|
const [filePreview, setFilePreview] = (0, import_react.useState)(null);
|
|
127173
127335
|
const [floatingPreview, setFloatingPreview] = (0, import_react.useState)(null);
|
|
127174
127336
|
const [filePreviewError, setFilePreviewError] = (0, import_react.useState)("");
|
|
@@ -127189,6 +127351,8 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127189
127351
|
selectedInboxThread
|
|
127190
127352
|
]);
|
|
127191
127353
|
const agent = agentsById[selectedInboxThread] || null;
|
|
127354
|
+
const agentFinalizing = typeof agent?.meta?.lifecycleAction === "string" && agent.meta.lifecycleAction.startsWith("finalizing-");
|
|
127355
|
+
const { terminalOpen, terminalTarget, terminalOpening, openTerminal: handleOpenTerminal, closeTerminal: handleCloseTerminal } = useAgentTerminal(agent?.id);
|
|
127192
127356
|
const agentSpawnRequestId = typeof agent?.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : "";
|
|
127193
127357
|
const importedHistory = (0, import_react.useMemo)(() => {
|
|
127194
127358
|
return chatHistoryImports.filter((history) => history.targetAgentId === selectedInboxThread || Boolean(agentSpawnRequestId && history.targetSpawnRequestId === agentSpawnRequestId));
|
|
@@ -127269,18 +127433,6 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127269
127433
|
agent?.createdAt,
|
|
127270
127434
|
importedHistory
|
|
127271
127435
|
]);
|
|
127272
|
-
async function handleOpenTerminal() {
|
|
127273
|
-
if (!agent || terminalOpening) return;
|
|
127274
|
-
setTerminalOpening(true);
|
|
127275
|
-
try {
|
|
127276
|
-
setTerminalTarget(await api("POST", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session"));
|
|
127277
|
-
setTerminalOpen(true);
|
|
127278
|
-
} catch (e) {
|
|
127279
|
-
showError("Terminal Failed", e.message);
|
|
127280
|
-
} finally {
|
|
127281
|
-
setTerminalOpening(false);
|
|
127282
|
-
}
|
|
127283
|
-
}
|
|
127284
127436
|
const clearFilePreviewCloseTimer = (0, import_react.useCallback)(() => {
|
|
127285
127437
|
if (filePreviewCloseTimer.current === null) return;
|
|
127286
127438
|
window.clearTimeout(filePreviewCloseTimer.current);
|
|
@@ -127362,16 +127514,6 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127362
127514
|
setFilePreview(null);
|
|
127363
127515
|
setFilePreviewError("");
|
|
127364
127516
|
}
|
|
127365
|
-
function handleCloseTerminal(open) {
|
|
127366
|
-
if (open) {
|
|
127367
|
-
setTerminalOpen(true);
|
|
127368
|
-
return;
|
|
127369
|
-
}
|
|
127370
|
-
setTerminalOpen(false);
|
|
127371
|
-
const target = terminalTarget;
|
|
127372
|
-
setTerminalTarget(null);
|
|
127373
|
-
if (agent && target?.mode === "guest") api("DELETE", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session/" + encodeURIComponent(target.session)).catch(() => {});
|
|
127374
|
-
}
|
|
127375
127517
|
async function uploadFiles(files) {
|
|
127376
127518
|
const list = Array.from(files).filter((file) => file.size > 0);
|
|
127377
127519
|
if (!list.length) return;
|
|
@@ -127417,7 +127559,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127417
127559
|
setPendingAttachments([]);
|
|
127418
127560
|
}
|
|
127419
127561
|
function handleSend() {
|
|
127420
|
-
if (!draft.trim() && readyAttachments.length === 0 || !selectedInboxThread || chatSending || hasPendingUploads) return;
|
|
127562
|
+
if (!draft.trim() && readyAttachments.length === 0 || !selectedInboxThread || chatSending || hasPendingUploads || agentFinalizing) return;
|
|
127421
127563
|
const attachments = readyAttachments.map((item) => ({
|
|
127422
127564
|
artifactId: item.artifact.id,
|
|
127423
127565
|
kind: item.artifact.kind,
|
|
@@ -127583,52 +127725,11 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127583
127725
|
voiceTts.available && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
127584
127726
|
variant: "ghost",
|
|
127585
127727
|
size: "icon-sm",
|
|
127586
|
-
title: voiceTtsEnabled ? "Speaking agent responses aloud — click to mute" : "Speak agent responses aloud (active chat)",
|
|
127728
|
+
title: voiceTtsEnabled ? "Speaking agent responses aloud — click to mute (engine & voice in Settings)" : "Speak agent responses aloud (active chat) — engine & voice in Settings",
|
|
127587
127729
|
className: voiceTtsEnabled ? "text-primary" : "",
|
|
127588
127730
|
onClick: () => setVoiceTtsEnabled(!voiceTtsEnabled),
|
|
127589
127731
|
children: voiceTtsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Volume2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VolumeX, { className: "w-3.5 h-3.5" })
|
|
127590
127732
|
}),
|
|
127591
|
-
voiceTts.available && voiceTtsEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
127592
|
-
value: voiceTtsMode,
|
|
127593
|
-
onChange: (e) => setVoiceTtsMode(e.target.value),
|
|
127594
|
-
title: "Voice engine — Kokoro (server, natural) falls back to browser automatically",
|
|
127595
|
-
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
127596
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
127597
|
-
value: "kokoro",
|
|
127598
|
-
children: "Kokoro"
|
|
127599
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
127600
|
-
value: "browser",
|
|
127601
|
-
children: "Browser"
|
|
127602
|
-
})]
|
|
127603
|
-
}), voiceTtsMode === "kokoro" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", {
|
|
127604
|
-
value: voiceTtsKokoroVoice,
|
|
127605
|
-
onChange: (e) => setVoiceTtsKokoroVoice(e.target.value),
|
|
127606
|
-
title: "Kokoro voice",
|
|
127607
|
-
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
127608
|
-
children: [...KOKORO_VOICES, ...KOKORO_VOICES.some((v) => v.id === voiceTtsKokoroVoice) ? [] : [{
|
|
127609
|
-
id: voiceTtsKokoroVoice,
|
|
127610
|
-
label: voiceTtsKokoroVoice
|
|
127611
|
-
}]].map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
127612
|
-
value: v.id,
|
|
127613
|
-
children: v.label
|
|
127614
|
-
}, v.id))
|
|
127615
|
-
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
|
|
127616
|
-
value: voiceTtsLang,
|
|
127617
|
-
onChange: (e) => setVoiceTtsLang(e.target.value),
|
|
127618
|
-
title: "Voice language",
|
|
127619
|
-
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
127620
|
-
children: [[...new Set([
|
|
127621
|
-
"en-US",
|
|
127622
|
-
...speechLangs,
|
|
127623
|
-
...voiceTtsLang ? [voiceTtsLang] : []
|
|
127624
|
-
])].sort().map((l) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
127625
|
-
value: l,
|
|
127626
|
-
children: l
|
|
127627
|
-
}, l)), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
|
|
127628
|
-
value: "",
|
|
127629
|
-
children: "Browser default"
|
|
127630
|
-
})]
|
|
127631
|
-
})] }),
|
|
127632
127733
|
canOpenTerminal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
127633
127734
|
variant: "ghost",
|
|
127634
127735
|
size: "icon-sm",
|
|
@@ -127916,7 +128017,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127916
128017
|
}),
|
|
127917
128018
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
127918
128019
|
size: "icon",
|
|
127919
|
-
disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending,
|
|
128020
|
+
disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending || agentFinalizing,
|
|
127920
128021
|
onClick: handleSend,
|
|
127921
128022
|
className: "shrink-0 mb-0.5 rounded-xl h-[42px] w-[42px]",
|
|
127922
128023
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Send, { className: "w-4 h-4" })
|
|
@@ -127966,7 +128067,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127966
128067
|
})]
|
|
127967
128068
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
127968
128069
|
size: "icon",
|
|
127969
|
-
disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending,
|
|
128070
|
+
disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending || agentFinalizing,
|
|
127970
128071
|
onClick: handleSend,
|
|
127971
128072
|
className: "rounded-xl h-9 w-9",
|
|
127972
128073
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Send, { className: "w-4 h-4" })
|
|
@@ -127975,7 +128076,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
127975
128076
|
}),
|
|
127976
128077
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
127977
128078
|
className: "text-xs text-muted-foreground mt-1.5 hidden md:block",
|
|
127978
|
-
children: "Enter to send · Shift+Enter for newline"
|
|
128079
|
+
children: agentFinalizing ? `${agent ? displayName(agent) : "Agent"} is wrapping up (${(agent?.meta?.lifecycleAction).slice(11)}) — messaging paused` : "Enter to send · Shift+Enter for newline"
|
|
127979
128080
|
})
|
|
127980
128081
|
]
|
|
127981
128082
|
})
|
|
@@ -128104,13 +128205,10 @@ function AgentCard({ agent }) {
|
|
|
128104
128205
|
const openPairInvite = useRelayStore((s) => s.openPairInvite);
|
|
128105
128206
|
const openRename = useRelayStore((s) => s.openRename);
|
|
128106
128207
|
const openConfirm = useRelayStore((s) => s.openConfirm);
|
|
128107
|
-
const showError = useRelayStore((s) => s.showError);
|
|
128108
128208
|
const doAgentAction = useRelayStore((s) => s.doAgentAction);
|
|
128109
128209
|
const doDeleteAgent = useRelayStore((s) => s.doDeleteAgent);
|
|
128110
128210
|
const openFilesForAgent = useRelayStore((s) => s.openFilesForAgent);
|
|
128111
|
-
const
|
|
128112
|
-
const [terminalTarget, setTerminalTarget] = (0, import_react.useState)(null);
|
|
128113
|
-
const [terminalOpening, setTerminalOpening] = (0, import_react.useState)(false);
|
|
128211
|
+
const { terminalOpen, terminalTarget, terminalOpening, openTerminal: handleOpenTerminal, closeTerminal: handleCloseTerminal } = useAgentTerminal(agent.id);
|
|
128114
128212
|
const pairsMap = usePairsByAgentId();
|
|
128115
128213
|
const rawAttention = useAgentAttention(agent);
|
|
128116
128214
|
const pair = pairsMap[agent.id] || null;
|
|
@@ -128124,28 +128222,6 @@ function AgentCard({ agent }) {
|
|
|
128124
128222
|
const canOpenTerminal = Boolean(agent.providerCapabilities?.terminal?.live?.read || agent.providerCapabilities?.terminal?.attach?.create);
|
|
128125
128223
|
const canWriteTerminal = Boolean(agent.providerCapabilities?.terminal?.live?.write || agent.providerCapabilities?.model?.provider === "claude");
|
|
128126
128224
|
const canForget = agentCanBeForgotten(agent);
|
|
128127
|
-
async function handleOpenTerminal() {
|
|
128128
|
-
if (terminalOpening) return;
|
|
128129
|
-
setTerminalOpening(true);
|
|
128130
|
-
try {
|
|
128131
|
-
setTerminalTarget(await api("POST", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session"));
|
|
128132
|
-
setTerminalOpen(true);
|
|
128133
|
-
} catch (e) {
|
|
128134
|
-
showError("Terminal Failed", e.message);
|
|
128135
|
-
} finally {
|
|
128136
|
-
setTerminalOpening(false);
|
|
128137
|
-
}
|
|
128138
|
-
}
|
|
128139
|
-
function handleCloseTerminal(open) {
|
|
128140
|
-
if (open) {
|
|
128141
|
-
setTerminalOpen(true);
|
|
128142
|
-
return;
|
|
128143
|
-
}
|
|
128144
|
-
setTerminalOpen(false);
|
|
128145
|
-
const target = terminalTarget;
|
|
128146
|
-
setTerminalTarget(null);
|
|
128147
|
-
if (target?.mode === "guest") api("DELETE", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session/" + encodeURIComponent(target.session)).catch(() => {});
|
|
128148
|
-
}
|
|
128149
128225
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
|
|
128150
128226
|
className: "group hover:border-zinc-600 transition-colors cursor-pointer",
|
|
128151
128227
|
onClick: () => openAgentDetail(agent),
|
|
@@ -154222,30 +154298,101 @@ function WorkspaceSettings() {
|
|
|
154222
154298
|
});
|
|
154223
154299
|
}
|
|
154224
154300
|
function VoiceSettings() {
|
|
154301
|
+
const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled);
|
|
154302
|
+
const setVoiceTtsEnabled = useRelayStore((s) => s.setVoiceTtsEnabled);
|
|
154303
|
+
const voiceTtsMode = useRelayStore((s) => s.voiceTtsMode);
|
|
154304
|
+
const setVoiceTtsMode = useRelayStore((s) => s.setVoiceTtsMode);
|
|
154305
|
+
const voiceTtsKokoroVoice = useRelayStore((s) => s.voiceTtsKokoroVoice);
|
|
154306
|
+
const setVoiceTtsKokoroVoice = useRelayStore((s) => s.setVoiceTtsKokoroVoice);
|
|
154307
|
+
const voiceTtsBrowserVoice = useRelayStore((s) => s.voiceTtsBrowserVoice);
|
|
154308
|
+
const setVoiceTtsBrowserVoice = useRelayStore((s) => s.setVoiceTtsBrowserVoice);
|
|
154225
154309
|
const voiceInputMode = useRelayStore((s) => s.voiceInputMode);
|
|
154226
154310
|
const setVoiceInputMode = useRelayStore((s) => s.setVoiceInputMode);
|
|
154311
|
+
const [browserVoices, setBrowserVoices] = (0, import_react.useState)(() => availableSpeechVoices());
|
|
154312
|
+
(0, import_react.useEffect)(() => {
|
|
154313
|
+
if (typeof window === "undefined" || !("speechSynthesis" in window)) return;
|
|
154314
|
+
const refresh = () => setBrowserVoices(availableSpeechVoices());
|
|
154315
|
+
refresh();
|
|
154316
|
+
window.speechSynthesis.addEventListener?.("voiceschanged", refresh);
|
|
154317
|
+
return () => window.speechSynthesis.removeEventListener?.("voiceschanged", refresh);
|
|
154318
|
+
}, []);
|
|
154319
|
+
const kokoroOptions = KOKORO_VOICES.some((v) => v.id === voiceTtsKokoroVoice) ? KOKORO_VOICES : [...KOKORO_VOICES, {
|
|
154320
|
+
id: voiceTtsKokoroVoice,
|
|
154321
|
+
label: voiceTtsKokoroVoice
|
|
154322
|
+
}];
|
|
154227
154323
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
|
|
154228
154324
|
className: "space-y-3 rounded-lg border p-4",
|
|
154229
|
-
children: [
|
|
154230
|
-
|
|
154231
|
-
|
|
154232
|
-
|
|
154233
|
-
|
|
154234
|
-
|
|
154235
|
-
|
|
154236
|
-
|
|
154237
|
-
|
|
154238
|
-
|
|
154239
|
-
|
|
154240
|
-
|
|
154241
|
-
|
|
154242
|
-
|
|
154243
|
-
|
|
154244
|
-
|
|
154245
|
-
|
|
154246
|
-
|
|
154325
|
+
children: [
|
|
154326
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
154327
|
+
className: "flex items-center justify-between gap-2",
|
|
154328
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
154329
|
+
className: "flex items-center gap-2",
|
|
154330
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Volume2, { className: "w-4 h-4" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
154331
|
+
className: "text-sm font-semibold",
|
|
154332
|
+
children: "Voice"
|
|
154333
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
154334
|
+
className: "text-xs text-muted-foreground",
|
|
154335
|
+
children: "Speak agent responses aloud in the active chat, and how push-to-talk behaves."
|
|
154336
|
+
})] })]
|
|
154337
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch, {
|
|
154338
|
+
checked: voiceTtsEnabled,
|
|
154339
|
+
onCheckedChange: setVoiceTtsEnabled
|
|
154340
|
+
})]
|
|
154341
|
+
}),
|
|
154342
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
|
|
154343
|
+
label: "Speech engine",
|
|
154344
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
|
|
154345
|
+
value: voiceTtsMode,
|
|
154346
|
+
onValueChange: (v) => setVoiceTtsMode(v),
|
|
154347
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
154348
|
+
value: "kokoro",
|
|
154349
|
+
children: "Kokoro (server, natural — falls back to browser)"
|
|
154350
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
154351
|
+
value: "browser",
|
|
154352
|
+
children: "Browser (Web Speech API)"
|
|
154353
|
+
})] })]
|
|
154354
|
+
})
|
|
154355
|
+
}),
|
|
154356
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
|
|
154357
|
+
label: "Kokoro voice",
|
|
154358
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
|
|
154359
|
+
value: voiceTtsKokoroVoice,
|
|
154360
|
+
onValueChange: setVoiceTtsKokoroVoice,
|
|
154361
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: kokoroOptions.map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
154362
|
+
value: v.id,
|
|
154363
|
+
children: v.label
|
|
154364
|
+
}, v.id)) })]
|
|
154365
|
+
})
|
|
154366
|
+
}),
|
|
154367
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
|
|
154368
|
+
label: "Browser voice",
|
|
154369
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
|
|
154370
|
+
value: voiceTtsBrowserVoice || "__default__",
|
|
154371
|
+
onValueChange: (v) => setVoiceTtsBrowserVoice(v === "__default__" ? "" : v),
|
|
154372
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
154373
|
+
value: "__default__",
|
|
154374
|
+
children: "System default"
|
|
154375
|
+
}), browserVoices.map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
154376
|
+
value: v.uri,
|
|
154377
|
+
children: v.label
|
|
154378
|
+
}, v.uri))] })]
|
|
154379
|
+
})
|
|
154380
|
+
}),
|
|
154381
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
|
|
154382
|
+
label: "Push-to-talk input",
|
|
154383
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
|
|
154384
|
+
value: voiceInputMode,
|
|
154385
|
+
onValueChange: (v) => setVoiceInputMode(v),
|
|
154386
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
154387
|
+
value: "compose",
|
|
154388
|
+
children: "Fill the message box (review, then Enter)"
|
|
154389
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
154390
|
+
value: "autosend",
|
|
154391
|
+
children: "Send immediately (speak-and-send)"
|
|
154392
|
+
})] })]
|
|
154393
|
+
})
|
|
154247
154394
|
})
|
|
154248
|
-
|
|
154395
|
+
]
|
|
154249
154396
|
});
|
|
154250
154397
|
}
|
|
154251
154398
|
var EFFORTS = [
|
|
@@ -159927,6 +160074,10 @@ if ("serviceWorker" in navigator) {
|
|
|
159927
160074
|
min-width: calc(var(--spacing) * 0);
|
|
159928
160075
|
}
|
|
159929
160076
|
|
|
160077
|
+
.min-w-4 {
|
|
160078
|
+
min-width: calc(var(--spacing) * 4);
|
|
160079
|
+
}
|
|
160080
|
+
|
|
159930
160081
|
.min-w-5 {
|
|
159931
160082
|
min-width: calc(var(--spacing) * 5);
|
|
159932
160083
|
}
|
|
@@ -160981,6 +161132,16 @@ if ("serviceWorker" in navigator) {
|
|
|
160981
161132
|
}
|
|
160982
161133
|
}
|
|
160983
161134
|
|
|
161135
|
+
.bg-background\/95 {
|
|
161136
|
+
background-color: var(--background);
|
|
161137
|
+
}
|
|
161138
|
+
|
|
161139
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
161140
|
+
.bg-background\/95 {
|
|
161141
|
+
background-color: color-mix(in oklab, var(--background) 95%, transparent);
|
|
161142
|
+
}
|
|
161143
|
+
}
|
|
161144
|
+
|
|
160984
161145
|
.bg-black\/0 {
|
|
160985
161146
|
background-color: #0000;
|
|
160986
161147
|
}
|
|
@@ -161851,10 +162012,6 @@ if ("serviceWorker" in navigator) {
|
|
|
161851
162012
|
font-size: .8rem;
|
|
161852
162013
|
}
|
|
161853
162014
|
|
|
161854
|
-
.text-\[8px\] {
|
|
161855
|
-
font-size: 8px;
|
|
161856
|
-
}
|
|
161857
|
-
|
|
161858
162015
|
.text-\[9px\] {
|
|
161859
162016
|
font-size: 9px;
|
|
161860
162017
|
}
|
|
@@ -163469,6 +163626,10 @@ if ("serviceWorker" in navigator) {
|
|
|
163469
163626
|
}
|
|
163470
163627
|
|
|
163471
163628
|
@media (min-width: 40rem) {
|
|
163629
|
+
.sm\:block {
|
|
163630
|
+
display: block;
|
|
163631
|
+
}
|
|
163632
|
+
|
|
163472
163633
|
.sm\:flex {
|
|
163473
163634
|
display: flex;
|
|
163474
163635
|
}
|